Bad code, good code

Bad code, good code

Good code

Writing code isn't easy and writing good code is much harder. My older brother once told me that you may play hundreds of games of chess and you won't acquire the wisdom you would by reading a single book on the subject. The book compresses the experience of millions of games, thoughts, and ideas that were developed, used, and tested by great players. The same is true about coding. It's not simply a matter of experience, but also of reading, studying, learning from great minds lessons built over years of experience. That's why I highly value principles such as the Zen of Python (the first set of principles I learned) and the Unix Philosophy (the second) and the SOLID principles. These are immensely valuable lessons that no programmer should underestimate.

Bad code

I'd like to talk a bit about bad code though. Some terms have been created to describe bad code and, at least for my culture, they are very pejorative. Also, there is a movement that rightly tries to push developers into taking responsibility for writing good quality software and, in doing so, heavily bash developers who write "code that smell".

I am not comfortable with this. Taking responsibility is something that resonates pretty well with my values and cultural background, but all those fingers pointing at those who wrote legacy code bugs me a lot. My boss asked me to take a course on software architecture and among the lessons, it talks about spaghetti and smelly code, two rather offensive adjectives, and states that there is absolutely no excuse for writing bad code. No time constraints, budget constraints, pressure, or limited knowledge can excuse you, they say. Bad code is the programmer's fault and the team's fault. Period. The course invites developers to take responsibility upon themselves for delivering within time and schedule high quality, flawless software while learning what is needed in their spare time or admitting they're not fit for the job.

Well, this is too much to ask of anyone. It inspired me to write this article in defense of bad code. Here are three cases of bad code I have experience with. Two of them I was assigned the mission to maintain and evolve and I am directly responsible for the third.

Working under pressure

One of the excuses the developer should not offer is that of the pressure. Here is a true story that explains why I disagree.

The Executive Office of the President of Brasil was assigned the task of running a background check of every newly appointed high-rank public officer. Each nomination should be sent to auditing and intelligence bodies and their findings should be categorized and a decision on whether it was an acceptable nomination should be made based on some given criteria. The result was a terribly slow process that could take months for a single nomination. A quicker solution was proposed: a fast track where public officers with a proven successful track record could be nominated with a smaller set of criteria.

The implementation of this Fast Track was added to the assignments of a team member. One of the busiest ones. While no automated solution was in place, he had also to compute the list of fast track candidates once per day by hand, a task that would consume three hours every day. That's on top of the regular meetings and his other tasks. He accomplished it by working longer hours. Way over twelve hours a day. All this under a harsh corporate environment.

Here comes the bad code. He used the tools he knew, Excel and QlikView, and built a solution. The result was a partially automated solution that did the job in about half an hour. This was the situation I met when I joined the team. The most dedicated of my teammates had to spend half an hour of his free time at work every single day to save months of useless bureaucratic work. The QlikView Script code he wrote was not elegant, not pythonic, did not follow the SOLID principles, and was certainly not the best tool for the job. But it provided huge benefits for the organization at the cost of his own personal time.

Now I read working under pressure is no excuse for bad code. That's not true. Under pressure to deliver the end product every single day, the best strategy is not to postpone the automation until one finds the right tool, choose the best framework, or learn about best practices. Any code that could allow him to arrive 10 minutes earlier at home was a good code. It really didn't matter that it had repetitions of lacked documentation.

It did not take us much time to convert that routine to a python script, reducing the daily time spent to about five minutes. We could do it with much less pressure because his "bad code" was at work and we could take our time. Eventually, we managed to incorporate the routine into the main software and had a fully automated solution with much better code.

The "bad code", built under heavy pressure in extra hours of a dedicated employee converted a 3-hour task into a half-an-hour task. The less bad code converted this half-an-hour task into a 5 minute one. The good code converted the 5-minute task into an automated one. Each one of them had the previous solution to rely upon, had a better time schedule, and more people involved. That was not only a good "bad code". It was a heroic one.

Working with limited time

Let's move to another of the non-acceptable excuses for bad code: time constraints. I have a story about that too. It involves a twelve-hundred line SQL procedure I was assigned to maintain. Brazilian Federal Government has many buildings and real-estate properties that may be used, under different circumstances, by private citizens and organizations. This usage involves different kinds of payments and all of this is managed by a relatively large code base built over many years involving at least three different development teams.

It often happens that a judicial dispute results in a court decision to alter some characteristics of the debts. Usually, the court decision states that it must be implemented in no longer than a given number of days, under the penalty of a heavy daily fine or the imprisonment of the responsible authority. To complete the picture, the team knows nothing about the judicial dispute until a decision is made and some days may be wasted in bureaucratic procedures before someone is assigned to implement a solution.

Over time, a SQL procedure grew and improved, as somewhat similar judicial decisions arrived. It incorporated new use cases and became increasingly complex. At some point, it was considered a feature of the system. The developer responsible for it eventually got a better offer and left the team. When the next judicial decision arrived, it was up to us to deal with it. This is the context where I met this long, unindented, full of repetition code. I also had little experience with pl/pgSQL It took us more than three weeks to "fix" it. Initially, we were asked to merely execute the procedure. Then, to fix it as it was supposedly producing wrong results. It was only after two weeks that we realized that the true issue was that they wanted to implement a new feature into the procedure.

This code would surely qualify as spaghetti according to the course offered to us. But it did its job well for years, maintained by a single team member that was overwhelmed with other tasks and each change to the code should be implemented in a matter of few days.

I believe had our boss been aware that three weeks of the time of two developers would have to be spent in improving that code base he wouldn't mind at all. That's a very cheap price to pay in the face of the alternatives.

That "spaghetti" code implemented the court decisions within time. It could use some indentation, some documentation, and some separation of concerns, of course. That's what we've done now. It is now a collection of several different functions, each much smaller and simpler, and none of which were required by a judge to be implemented in three days.

Time constraints are indeed an obstacle to writing good quality code. I'm quite happy to have been the one to "fix" it with a decent time frame instead of the poor guy who struggled to understand the database structure and create the procedure with the clock ticking by his side.

Working with limited knowledge

The developer is also not supposed to argue that his lack of knowledge led to the bad code. They told us that it is our responsibility to learn our craft and our boss is not paying us to learn, but to "solve his problem". I have a hard time accepting this also. Learning is a great part of all jobs I've had. Also, I was actually being paid to learn that I am not being paid to learn my craft. This simply does not make sense.

I chose a different story to illustrate my point here. A story where I was both the boss and the developer.

My brother and I decided to start a game development studio to develop open-source table-top and computer games. A media with the PC game would be shipped together with the table-top games bought. Our first game was also the first computer program I wrote and an achievement I am truly proud of.

In building that game I learned about software internationalization and used Gettext to achieve that. I learned SQL and used SQLite as the database. I learned about the Zen of Python and had my first experience writing a game from scratch. I learned about version control systems and used Bazaar. I learned about profiling and how to tackle performance issues. I learned many of python's features such as lambda functions, list comprehension, and generators. But I especially learned with the poor choices I've made. The code is barely testable, hard to maintain and violates many principles. One would call it a nightmare, but it made my dream of developing a game come true and gave me the confidence I needed to move forward.

That experience taught me that to code is to learn. Each time I chose to research and learn before implemented a feature I had a much better result than when I chose to proceed with what I already knew. Everything I learned would inspire me to refactor what I had written.

Good code requirements

It is not easy to write software and writing scalable, maintainable easy to read code is even harder. Perhaps instead of pointing fingers at the developers who wrote the smelly code we suffer from, we could listen to their "excuses". They reveal the causes of bad code and the path to improvement.

Pressure. A favorable environment where one can have peace of mind to think about a problem and understand it in detail is a hard requirement for good code. Instead of pushing the developers with threats and harsh words, a respectful environment with clearly specified and achievable goals could do a better job.

Deadlines are commonly set without any good knowledge of the effort needed and meeting such arbitrary dates is considered a success, while postponing to deliver a good product is a failure.

It makes little difference to teach developers they should deliver high quality under pressure. It won't work. Elite athletes make stupid mistakes under pressure. I am thinking about the top 3 athletes of any sport. The world's most famous Go player made mistakes under pressure while playing against AlphaGo. Most companies do not have access to this kind of talent and yet expect their employees to write elegant code under pressure. No wonder it fails.

Time. No one writes good code in one sitting. The code improves as the developer writes it. A single class becomes two. A single method becomes several. An if statement grows and becomes a switch, continues to grow and becomes a router or, eventually becomes a separate service altogether. Variables change the scope and move around the project seeking their place. New libraries are added and removed and even the most basic design decisions may be reverted as the project unfolds. This takes time.

If the time constraints are tight, there is a pressure to stick to the previous decisions. All previous decisions were made when there was less information available. At each new step, we are in a better position to make a decision. After writing a class we know much better if that class should have been written in the first place. This means that sticking to previous decisions is to abandon all the knowledge we create in the development process.

I think that is unwise. Developers are right to state that given more time, especially early in the process, will result in better quality software. It allows for experimentation, for learning, for better decisions.

Knowledge. Learning while you build is a necessary feature of software development. I have learned something new with each and every single project I've worked with. The simplest of the scripts I write is an opportunity to learn something. Asking the developer not to invest time to learn during working hours is the worst mistake a project leader can make. Good code is not simply the product of accumulated previous knowledge. Every project is different. Good code is the result of knowledge accumulated and built during the project.

I've been working with web development for more than 10 years and I have no hope - and no need - of knowing the names of all the important libraries out there. Developers are constantly learning and this does not mean that they are going to conferences and taking courses. It means that coding is an activity that involves research, experimentation, and learning.

Stoic code

Writing good code is not a matter of writing an award-winning piece of literature. There is a context for each piece of code. It involves a time frame, a budget, limited business knowledge, limited technical knowledge, business culture, pressure, and much more. An interesting aspect of stoic ethics is to disregard as morally indifferent anything that is out of one's control or influence. There are many aspects of that context that are completely out of the control of the developer. These include time and budget requirements. It may be useless to fight them if you have no influence over those who make these decisions. The same may be said about the languages used and other hard requirements.

Stoicism teaches one should be concerned with those things that are under his control. That's where you can improve and make a difference. This is an interesting thought for a developer. I may not be able to implement a test-driven culture in my organization, but I may still be able to improve my code in that direction, by including some tests of my own or at least assert statements. Dealing with time and pressure is harder. These things may be also out of our control and there may be little one can do to improve the quality of a piece of software that must be delivered as soon as possible.

My advice would be to maintain peace of mind at all times. Write tests and refactor if possible. Try to write code you wouldn't be ashamed to show in a job interview if the interviewer knew the context it was written in. Finally, being part of an organization is something that is within our control. If you are not in a position to influence the time frame or change a harsh corporate culture that leads you to write bad code, this may be damaging your long term career. Seeking a different position may be a much better use of your spare time than dedicating extra-hours to the current client or employer.



To view or add a comment, sign in

More articles by Nelson O.

  • Construa um mini-site JAM-Stack e ajude uma causa!

    Que tal dedicar algumas horas do seu tempo para aprender um conjunto de tecnologias modernas enquanto constrói um site…

  • The power of a tiny language

    Both as a teacher and as a student I enjoy using dot to generate graphs. Due to it's single purpose, it can be quite an…

  • Writing Java with Vim

    I came from scripting languages I have the good fortune to be a software developer. I first learned to program with…

  • Is XML human-readable?

    I've read it in many tutorials, articles, and blog posts: "XML is not human-readable". It is supposedly full of useless…

    1 Comment

Explore content categories