We'll have to go over this again later on…

The legacy code trap
10
Apr

We’ll have to go over this again later on…

In addition to the many large and long-standing legacy code applications, there are more and more brand-new products. New extensions and modules offer new possibilities and thus opportunities for customers and companies. However, there is one major problem that continues to produce legacy code: the developer. In this article I will highlight various aspects of software development and encourage good software quality and work.

That’s a complete mess, I would love to rewrite everything”, muttered the trainee next to me as he struggled with a TYPO3 extension of a current project. This sentiment is growing in popularity. But there are, in fact, always new developments – no matter whether they are called modules, extensions or plug-ins. It is tempting to be able to prove oneself and work according to one’s own ideas. When we are highly motivated, we work better. In times of Composer, even a wide range of external libraries can be implemented without great effort. Web development today ultimately demands the right technological solution for every application. But appearances are deceiving because where there is nothing at the beginning of development, there is one thing in particular: a lot to do.

 

Every start is hard

New technologies as well as new versions require a lot of experience and are by no means self-running. But if you don’t have any specific experience yet, you have to acquire it quickly. This means that developers are not only fighting against the project deadline but also against their own impatience. Maybe they bet on the wrong horse after all…? For the time being, a developer can be forced to build an up-and-running version using Google and Stack Overflow – doing exactly what you didn’t want to do.

This is what developers do up to a certain point on because, despite all this, you can still achieve a good result that offers a lot of potential. However, it is a result with limitations and the negative consequences of this experimental phase include user statements that are no longer used, variable and method names that no longer apply as well as classes that have become too large. Although it is evident that work is and has been in progress, this alone is no sign of software quality. But one thing is certain: scalability and maintainability are two central goals of good software development that cannot be implemented afterwards.

Discover the program of IPC ’18!

 

The Big Picture

Small, large and even sub-projects need structure. How many files and how many lines of code do I need? However, I keep noticing developers who just start coding without hesitation. That’s how things get messy. One could call those mostly young developers Trial-and-Error-Coders. In the course of a project, not only the structural chaos of the files but also the source code becomes increasingly chaotic, causing unpredictable things to occur. Above all, this often concerns data manipulation and requires experience: data must be validated, modified, enriched or cleaned and that is consequently based on user input. Developers tend to underestimate this.

In this respect, it is important to have good project management that ensures that actual and exhaustive data are available at an early stage. However, new requirements or problems often arise regardless. “Make it run” then suddenly becomes the key phrase – the deadline must be met howsoever! This is at the developer’s expense, of course. Nonetheless, in order to achieve the goals set for the day, small “failures” are sometimes acceptable. The reasons behind this often vary and cannot always be explained. For example, an application runs locally but not on the staging system or a large amount data can suddenly cause problems.

In this case, you have put the right things and in the right place and do it quickly. You need to keep calm and have a few files with only a small amount of code in a clear structure. Only then will you be able to react effectively and flexibly to such circumstances. Tinkered code, however, is very long and usually contains a considerable number of if- statements. Low readability and high complexity can, however, slow down further development considerably. For the aforementioned reasons, though, the code was not designed to be sustainable but only to achieve any number of the various daily goals. This is precisely the flaw that will have a negative impact on all future prospects. Developers should always avoid this and write clean and high quality code right from the start!

 

Executable code is not the benchmark of success

As web developers, we are in a constant predicament – on the one hand there is the need for on-time completion and on the other hand the need for scalability of an application. In many cases, however, the initial focus will only be on the cleanness of the code, i.e. refactoring, clean-up or tidying up. Scalability is usually still a long way off, at this point. From my point of view, this is fundamentally wrong, and the blame lies solely with developers. One thing is quite clear: bad code is written by bad developers. No excuses shall be offered here! But if nobody pays attention to such a code, nothing can be seen nor judged.

Source code should always be written cleanly and in an easy-to-read fashion, right from the start. Everything else is extremely inefficient and costly, even if it just happened by chance. That’s precisely one of the big traps related to legacy code. “Any fool can write code that a computer can understand. Good programmers write code that humans can understand,” says software architecture expert Martin Fowler. That sums it up nicely.

I write good code for myself and for my colleagues. 90 percent of the work consists of reading code and 10 percent of writing it. And that’s a good ratio; with bad code you read even more and hardly write anything at all. The general rule goes as follows: If I cannot read and understand my code well and quickly, I cannot work effectively. Additionally, the actual code becomes more complex over time as new requirements and unexpected challenges make it grow. One consequence of this is that methods have to “accomplish” more than their actual task.

Data manipulation has to be the keyword for the numerous specific cases in if-statements. This can also apply to variable names that are simply no longer valid. Nested loops and statements can even build up a considerable complexity that cannot be understood by simple reading without debugging. The application still runs, despite the fact that external sources that are no longer used are included. Database fields and much more can become obsolete. Another very poor idea is commented out code blocks which require continuous refactoring – preferably with the opinion of at least one more developer. Otherwise what Martin Fowler says will come true: “Later equals never“.

This means that if developers don’t clean up after themselves, legacy code is often generated in a matter of hours. As a result, applications become no longer maintainable and thus very expensive – in terms of maintenance. Typical legacy traps include:

  • growing methods
  • variable names
  • if-statements
  • loops
  • Dependencies
  • Responsibilities
  • Name Designations
  • unused things

 

Executable code is not the benchmark of success

Many web developers aim towards fully automated systems. Their goal is to develop a dynamic application that adapts itself to all future requirements. Multilingualism, multi-domain and multi-client capability are some features that are subsequently only implemented in “consultation with oneself”. This means that a web developer makes technical decisions, independently of the interest of the customer, which lead to unnecessary development efforts and increase the risk of bugging. This requires configurations that load special dependencies for the use case. “To keep it clean,” is usually the reasoning.

But in fact, this is just wrong and dangerous. An agile development approach that ensures features are kept tight and can be delivered quickly is more appropriate here. In addition, the developer receives feedback and can continuously advance the whole project even further. In a nutshell, you have to part with the idea that software will be finished sometime, because then it is simply dead. But as long as it is in use, it needs further development. This is due to external influences such as updates and new requirements brought about by the market.

 

Project management is important

The longer a development step is delayed, the greater the requirements and expectations for the result, all the more reason to deliver quickly and not get confused. Unfortunately, this is precisely the phase during which sales often continue, regardless of internal resources. This means that there are other concurrent requirements on a current task. However, if the foundation is not yet solid and durable, the project is seriously endangered. Of course we always talk about some small new requirements, which come from the customer and in any case have to be included, e.g. a newsletter (also as text version), images that can be downloaded for high resolutions, SEO URL special cases or similar. “That doesn’t have to be included yet, but should be taken into account,” the customer says and every developer knows what’s hidden behind the words is “It would be better if it was done“. This suddenly puts a lot of pressure on the developer because he does not know how to protect himself and in reality he/she may already have lost him/herself. Now, however, a “higher authority” has already decided that it is possible and has decided on how long it will take or at least when it should be ready: by today.

Diamonds are created under pressure – but good software quality cannot be achieved like this. It must therefore be made clear to the customer that it is also better for him to have the current task completely processed first and then give feedback. After that, you can think about additional features. Incidentally, it is not as relevant as one might think to communicate information on future requirements at an early stage. If agile software development achieves a small goal with minimal effort, you are very flexible. This means that you can always respond to future requirements. They always come anyway. So it makes no difference whether you know about them today or next week. Instead, two tasks or perhaps only one and a half tasks are implemented simultaneously. That’s not good. You should protect yourself from this by creating clearly prioritized tickets.

 

The dirty business of support hours

There are very different types of developers. Pioneers and keepers are perhaps the most famous. Some want to see something quickly; others want to sustainably improve the entire software. But when both work independently, many problems and, above all, too much code arises. Pioneers (or boot-strappers) run as fast as they can along the first path they find until they reach their destination. Unfortunately, they don’t care about the length of the path, i.e. the number of lines of code. The goal is the goal. To be able to click and see it, that’s what counts. In some cases, new technologies are used of which nobody has heard anything about and which nobody can adopt internally. Nowadays, everything is API-based because that is the new way on the Internet and microservices are the Holy Grail. But the most important thing is: The customer is enthusiastic. Everything is so fast and beautiful. “It can be revised later – but for now it is finished and running.

Quite frankly, it has to be said that this type of development is in great demand, especially in agencies. After all, this means that they enter the long-awaited support phase in which a customer can be properly milked financially. Additional hours are suddenly an extremely profitable business. Since such irresponsible business practices have a negative impact on Germany as a business market, we must break out of this pattern together.

 

Create your own tickets

New requirements equal new tickets. Transparency protects us. “The feature didn’t finish because the customer kept coming up with new requirements.” As a developer you have to make sure that this can be proven; not in CC mails with endless communications. Therefore, it is strongly recommended to turn a request into a task and then turn it into a ticket. That’s the story. What has been agreed upon must be documented. And the commitments must be booked onto the ticket. This makes the workload transparent and enables good work in the first place. Everything else is rather “on the fly”, i.e. not conclusive and therefore vague. What exactly did the customer mean and what is still missing?

Tickets, on the other hand, can be used to clarify issues and delegate tasks. These tickets can then be estimated separately and it is easy to tell exactly what needs to be done by the next deadline. “Getting things done – GTD” is a principle of self-management that can also be applied to software development. Those who focus on their current task protect themselves against distractions and psychological damage by using tickets.

 

Stay on guard

Automated tests are important – but unfortunately not very common. If you don’t have any, you work without a breaker. As the application becomes more complex (but stays cohesive), any change is a potential bug for some part somewhere across application, especially since there are unavoidable intersections. That’s why you always have to try and reflect on your own code and question it. Some questions you can ask yourself about your code structure are:

  • Have I reached my goal?
  • What was necessary for it?
  • What can I improve?
  • What can I do more elegantly?
  • Where are potential bugs?
  • What would I need to cover with a mock object during a unit test?
  • What are possible edge cases in this scenario?
  • When does my method raise an exception?
  • Does the trainee understand what is happening here?

 

Refactor every step

CMD + K is the key combination in PhpStorm that is used to commit. I commit when I make successful changes and whenever my current state is operational. I’ll briefly describe what happens. It can be a change in one line or a more complex development step. Once I have changed enough code, I immediately refactor. Then I’m on my toes and know exactly what I need and what should happen.

However, at times mistakes can creep in. “Never touch a running system” is one of those completely unnecessary slogans that you almost certainly come across in everyday agency life. Therefore, reliable tests are recommended precisely for these cases. They save a lot of time and minimize the risk considerably. But refactoring is more than just formatting. Refactoring goals are first and foremost:

  • clearly and readably summarize logical processes
  • remove unnecessary statements
  • move the code further to the left
  • increase readability for other developers
  • document your method, also for your own understanding
  • Check the method’s visibility
  • Can I assume the method by its name at the location without knowing the code?

 

Relocating problems is not the solution

The code example shown in Listing 1 is taken from a recent article I wrote in a magazine about agile software development. The code is not made up, but originates in a Magento module, which was very susceptible to bugs. In this particular case, by the way, a rewrite was chosen. However, the code was developed in a training course of Entwicklungshilfe NRW and is jointly “refactored” in connection with PHPUnit as a team assignment. It is generally best to move the else-parts upwards, use early exits for validation and thus, among other things, resolve a nesting level. Naturally, the first if-statement catches the eye. No else-part is necessary since you get off at this point with a return. But you don’t have to see this passage first because every line is a bit of a disaster.

public function preparePrint($id)
{
  $returnedtag = Mage::getModel('glsbox/shipment')->getCollection()->addFieldToFilter('id',
$id)->getFirstItem(
)->getGlsMessage();
  if ($returnedtag === false || $returnedtag == "") { return false;
} else {
    $tags = $this->parseIncomingTag($returnedtag); if (is_Array($tags)) {
      $service = Mage::getModel('glsbox/shipment')->getCollection()->addFieldToFilter('id',
$id)->getFirstItem(
)->getService();
      if ($service == "business" || $service == "cash") {
        $glsService = Mage::getModel('glsbox/label_gls_business');
      } elseif ($service == "express") {
        $glsService = Mage::getModel('glsbox/label_gls_express');
      }
      if ($glsService != null) {
        $glsService->importValues($tags); return $glsService->getData();
      } else {
        return false;
        }
      } else { return false;
    }
  }
}

This is why one participant made a completely different suggestion: “I’d transfer this a bit first, because then it’s gone“. Wrong! Because bad code remains bad, even if it is moved. You may, of course, think about the method name and its task and then actually transfer the service part. But priorities are needed for this. It is similar to when the first aider arrives at the accident site: stop the bleeding, stabilize the circulation, make it transportable – not the other way round.

Sure, CMD + T lets you quickly transfer a code part into its own method. This is right and important when methods are very long and there is too much to do. But that is not the case here. If you had transferred anything in the first step, you would have created a wrapper method. There is no use case for that. What is transferred must not be made available to other methods.

In our case it is much worse, because at the very end another new method is added. Readability suffers greatly from this. The goal is to improve the existing code, not to do “just anything “. You have to be aware of this task and internalize it. It’s about improving the code instead of just distributing it.

 

Know-how is crucial

At the beginning of the year I decided to rewrite a TYPO3 extension instead of refactoring. Any decision of this kind must be well-considered. To rewrite everything is quite a tedious process. However, the current extension was not operational and had an extremely high number of bugs. The copy-past detector proved to be extremely valuable, and the many classes had many methods that were very similar. They differed only at the end of their lengthy names and in the way that data was retrieved from another table. Under normal circumstances it is sufficient to pass an array of parameters, one of which has the key table with the table name as a value. One method – and it can even react correctly to incorrect information. So it can always be improved. This is what makes innovations possible in the first place.

The initial extension had over 40.000 lines of code, extremely long list of files, folders and hooks. The new extension now has a slim 2.000 lines, runs much faster and every developer in the company understands what is happening very quickly. By the way, the developer of the original extension was a professed clean coder. But what makes you produce 40.000 lines of code without a single test? So please remember: copy and paste is a poor method! You have to be wide awake or better yet work with PhpStorm-Live-Templates. In this case, however, the latter is not a solution.

Never code alone!” must be our motto. Otherwise there is no exchange of feedback. This was the only way the catastrophe mentioned above could have happened… Who takes over and bears the responsibility? This can’t be the developer alone. In fact, this example shows how important external feedback is, as a sort of diplomatic method. However, good advice is expensive. It may be cheaper than the damage but who wants to charge a customer for it?

 

Tests save lives

Anyone who has understood the purpose of tests as a tool for effective web development is using them. Otherwise you live under a lot of pressure, with little time and a lot of unnecessary knowledge.

Let us take a closer look at these three statements. Pressure arises not only from the lack of time but also from personal demands and one’s own expectations. An application without tests will become buggy. In my API tests, for example, it happens time and time again that a change for one route causes an exception for the other. The tests run for 4.5 seconds on all routes and also validate returned data. Every feature (i.e. classifications or changes in the data structure) is covered by tests. This saves a lot of time. In fact, manual testing is no longer possible because not every feature for every route can be tested and evaluated manually with Postman. However, without tests, I would have a huge time deficit and no security – an open-heart operation, if you will.

 

Don’t speculate – deliver

It’s always important to deliver quickly and get feedback. Short iteration cycles rotate faster and move the whole project continuously. This means that new functions are quickly available and can be used for the sales process in the business model. Perfection is actually not that important, otherwise the whole Internet wouldn’t work. There are many advantages to this approach. Above all, however the work is much more effective and efficient. This makes investments worthwhile again and all involved parties have a higher project satisfaction. Manageable processes are easier to calculate and can be communicated and billed more easily. Therefore you should keep calm and carry on.

 

Your core domain

There’s the gas factory anti-pattern. Essentially, it deals with the fact that a gas station should primarily deal with the dispensing of gasoline and it doesn’t need many other unnecessary features. This could be a car wash, a kiosk and other modules. But what is the core domain of a gas station? To sale gasoline.

Large legacy code applications have similar problems. Sometimes the customer doesn’t even know what his core domain is. And it’s certainly not a newsletter or a payment system. But even if he knows it, it’s often not found in the source code. Domain-driven development was the buzzword of 2017, where there are clear priorities in development and planning and above all an encapsulation of this core domain, the actual business model and USP of all the other modules. On the one hand, it can be quickly separated from possible ballast and, on the other hand, functionality is protected and always guaranteed.

 

All depend on the set-up

Effective web development only works with the right tools and the right set-up otherwise you lose time in several places. And time is then missing from daily personal training and refactoring. Therefore, projects should run locally. The exceptions prove the rule but in the end the actual application should run on your own computer. Databases and assets such as images can still be made available remotely. But that also causes problems.

This is where Xdebug comes in, an enormously important and effective tool. However, many developers don’t use it and prefer to sneak around with var_dump(); exit; instead. These debug points are also often left out of the source code. Xdebug offers many advantages, especially since you can use it to change values during the runtime. This makes it much easier to map different cases instead of simulating them each time with many clicks in the frontend. Local development environments need to be reviewed and unified across the team. And please, do not wait for months for the promised Docker instances at the end! Developers should be prepared for this so that they can build up more know-how in the area of web development and their own applications.

 

Inspect the code and evaluate the output

There’s a lot of tinkering and fiddling in the backrooms of the IT department. And developers often get lost because they can no longer think clearly under the ever increasing pressure. A second opinion makes all the difference – release pressure, for instance. Instead, regarding tasks and tickets, team members often find themselves in the role of a lone wolf. What’s more, being introverted makes it unpleasant for them to ask for help. They often do not see the mutual personal benefits. Then there’s the management, which is reluctant to apply solutions such as pair programming because they only see the doubled cost factor and not the possibility of better software quality. But single programming costs even more and has a bad effect on the feel-good factor. The consequences of single programming are manifold:

  • a much longer training period for external programmers
  • the project has no scalability for completion
  • high performance pressure on a single employee
  • no targeted and efficient use of personnel
  • lack of know-how transfer
  • no further training during the operation

 

Gossip is poison to a company

The inner attitude and the personal motivation are also very important so that you can work efficiently and deliver good code. Unfortunately, this is often lacking in development departments. Also, the heads of the company and the management have long been aware of this: “It’s better to let gossiping employees go and release them immediately in the event of their dismissal. They are poison to the company“. Especially web developers gossip constantly about others and their code. It’s a kind of hobby in the scene but it’s also terrible and wrong. So just let it go and work better and cleaner. You live and go through this world every day. And if it’s bad then you will have a bad day every day. It makes you sick and that’s not cool at all.
With all of our scarce resources we have to start thinking about personnel consequences. By the way, the 40k line employee mentioned earlier, only gossips about everything during and outside working hours. I generally don’t know many gossiping developers who are particularly good. Personally, I don’t participate in such behavior. I am a software quality consultant; I see a lot of legacy code and even go there voluntarily. Gossip won’t get us any further, stop it and have a positive outlook. Besides, other employees suffer because of this behavior and as a result we have to take responsibility. This demands our professional attitude.

 

Hide-and-seek special case projects

In a company it’s a senior web developer’s duty to make sure that all projects are executable in their own development environment and they should know all of these deployment processes. There are of course always some exceptions because there are just too many projects, which cannot all be set up locally. Still, all important projects which generate revenue should already be subjected to this requirement. But now there are some projects which are a bit “tricky”. And usually there’s hardly any other reason for this. Still, “tricky” can be documented. But of course this won’t happen – there’s not enough time for that. Documentation should be something decent but instead seems just utopian.

And quite often a completely different circumstance is noticeable: missing DevOps knowledge. And of course, not every developer can perform the server setups. That’s the reason why they started performing locally. But sometimes they simply just don’t know how to do it anymore and the damage is substantial. At this point, you cannot help the developer anymore. Nonetheless, this is exactly where help is most needed and where the process must be depicted in a comprehensible way. It’s worthwhile and very important as well. There are some environments which are not allowed to shut down due to fear and that’s catastrophic. Most importantly, a board of executives is not supposed to turn a blind eye on these issues; it’s just like the saying goes: “Someone else is going to take care of this.

 

A Conclusion to the Legacy Code Trap

We need to become more professional and work more efficiently. As developers, we have a creative job in a constantly evolving environment and there are many opportunities to do a really good work and deliver good projects. Web development will pay off again, if it’s effective. Help and free yourselves from the Legacy Code Swamp. It’s in your hands and together with the community any lone wolf out there can do it. Always remember: “Never Code Alone“.

Stay tuned!

Behind the Tracks of IPC ’18

PHP Development
Best Practices & Application

Web Development
Web Development & more

Web Architecture
Concepts & Environments

Performance & Security
All about Performance & Security

Agile & DevOps
Agile & DevOps methodologies

JavaScript
All about JavaScript

Testing & Quality
All about Testing & Quality