Core fix from hell or “Why core fixes possibly never should exist”
Human beings. Imperfect perfection in all its facets. No surprise that there is no perfectly designed software that would fit everyone’s needs. Some might say the most effective way to drill a hole into a wall is using a bull dozer. Consequently, if a software does not what it should just modify its core files and everything’s fine. But…
NO… DO NOT DO THAT!
Applying modified code to vendor files sounds appealing. You will save time figuring out another way solving your problem. You will exactly affect the piece of code you want to. Maybe you will have a feeling of improving the vendor software’s code by your own changes. But…
STOP THINKING ABOUT IT NOW!
So-called “core fixes” have the benefit that…
OKAY… ENOUGH! ONCE AND FOR ALL!
As you might see, the programmer’s little devil inside is trying to find several reasons how core fixes will be the best way to get your goals. And often it’s a significant shortcut. But if there’s darkness there is even some light. This is why the tiny bit of a heavenly creature in every single programmer’s soul braces up to resist the temptation.
Let’s start from the very beginning… with a short definition:
Core fix — a neologism once invented by smart programmers to justify various modifications in a software’s code to instantly solve existing bugs. Further used to justify efficient shortcuts to solve more or less complex custom issues or tasks.
One thing in advance: We need to distinguish… if you find a bug in a third party software (or package or code) please let the author know by reporting an issue or immediately solving the bug by creating a pull request. If you need a solution right in the moment then you will have to fix that lines of code, sure. In that case please have a look to the bottom of this article. I will show you a simple way how to feel better with that ugly core fix ;)
But now let’s say you are a PHP developer creating plugins für Wordpress, Drupal, or any other third party software extendable via a plugin system. Your client wants you to extend the data model in some way or maybe you have to override some business logic to fit the company’s needs. Usually the software will provide ways to implement that. If you ever worked with Symfony you might know the following:
- Using the event system to react before or after a specific event (pre|post) using Listeners or Subscribers
- Decorating DI services
- Using service tags to get your code being collected on initialization
- and way more…
But what if you won’t find an elegant way to get your tasks done? Sometimes it actually happens that the software’s architecture won’t allow to get to that specific place you need to. Damn! Okay… we need a core fix, right? Ehm… I don’t think so!
It is always a matter of how to visualize the issue or the task. It depends on how you proceed. I agree, maybe the way you’d like to solve that task might not work in some case! And that sucks… but I am almost sure that there still is another solution. There has to be! Because…
Every core fix you create will cause in an innocent baby cat’s dead — promise!
Core fixes aren’t just a “…well that’s a pretty fast forward solution…” thing. They are evil, diabolic and straight outa hell! The deeper the file, class, function or line of code you intend to modify, the higher the risk of unwanted side effects. Your changes might affect not just your use case scenario the way you intended it, it might also affect several other scenarios you won’t have in mind. And listen, you modify a software you usually don’t know from head to toe. Possibly you modified a piece of code that has no code coverage. Believe me, you don’t want to have to realize that something isn’t working properly anymore in production environment.
And as if that weren’t enough reasons to keep you from killing innocent baby cats, modifying files in a software’s core or at least in any third party vendor will break the ability to simply update the component or software. Why? Well, consider how updating a package to a newer version works. Regardless of which install or update routine or package manager you use (composer, npm, yarn, etc.) it’s always happening the same way: If a newer version is available, the whole bunch of files will be downloaded to override the previous files. This means, if you edited some file to fit your needs that file might be removed or replaced after the update.
All of your changes. Lost. Forever? Not at all, but even if you would have a backup of your core fix file(s)… would you really remind yourself to restore any modification after updating the vendor software? What if the core fix for example has been created earlier this year and today you executed your package managers update routine. Honestly, wouldn’t you forget about that? And what if not you but one of your colleagues does the update and never heard of any modifications in a vendor file?
“Okay, I got it… but I can’t find another way than doing a core fix!”
As written before, there almost has to be another solution. Probably it’s not the easiest, not the cheapest and of course not the fastest. But there will ever be a way to get your stuff done without modifying files that are not made for being modified at all. And where there’s a will, there’s a way! Just lean back, take a deep breath and try to think about your approach.
I believe in you! :)
And in case all else fails and you have to implement a core fix, please be careful at all! There is a common paradigm called DOWN (Do Only What’s Necessary). In my own words: “Gosh! Just do what needs to be done, stupid!” And to go any further:
- restructure the class in a way you would love to see it. The less changes you make, the less side effects may appear.
- rename variables or methods just because you can. This is not your code, therefore you don’t know what the original author thought or intented while implementing those lines.
- refactor methods even if they are way too big or far too complex. It is not your task to refactor that code. If you think that needs to be done just create an issue in the software’s git repository or even create a pull request.
- remove stuff just because you think nobody would need that. As mentioned before, you usually don’t know the software or package from head to toe. Thus you might not be able to oversee all various kind of use cases.
- rearrange files and folders. If you do that, all may be lost! There’s nothing left to say.
- search for the line that needs to be modified and call a method outside of the core which contains your custom business logic. No matter if you dispatch a custom event, call a static method or a DI service. The less lines of code you change or add to the core file itself, the better.
- unit test your custom business code. Thanks to extracting all your custom business logic into a separate file you are able to write as many unit tests as needed. This will protect you from bad surprises.
- take care that the core modification you did will not disappear without recognizing. There’s nothing worse than not realizing that your core fix has gone away because one of your colleagues did an update. Write a simple unit test, read the file you modified and verify if your changes in line x exist.
Let’s have a look on some example stuff.
First we identify the line of code inside the vendor class that needs to be modified and call our extracted custom business logic there. Have a look at line 5 inside the example code snippet below:
After that we will create our own business logic as mentioned above. Once again, the more unit tests you write to ensure your logic works properly, the better:
Now we take care of our changed line inside the core file. To do that, we create a simple unit test as you might see below. Programmatically read the file you changed and compare the modified line to make sure it’s equal to your test case’s expectation:
As soon as someone changes or updates the modified core file your test will fail and that is by far the best thing that might ever happen! This allows you to get noticed about the potential loss of your core fix.
And I’d go one step further:
After the vendor package got updated, maybe your core fix will not be needed anymore because the author for example refactored the code in a way that you will be able to solve your task in a more elegant way than by the help of a core fix.
Wouldn’t that be awesome?
To put it in a nutshell — in most cases software has been created just to fit the most common clients needs. If your specific use case may not work by default, don’t just modify the software core. Find a solution that protects you from touching core files, classes or functions. Core fixes are risky, ugly and devilish. Read the docs for finding a way to solve your issues (maybe by using the event system, the DI service container, etc.), ask the community for help or suggest some changes to the author. The less core fixes exist, the more innocent baby cats will cuddle with you ;)
More from me — If you like, please check it out:
Being a better programmer than this morning — some aspects to focus on