How Not to Develop Software
I’ve never seen two programmers develop the same program in the same way. Saying what’s right in software development is very difficult, it all depends on the currents of thought you follow.
Object-oriented programming, script fanatics, functional programming? There’s room for everyone and no one escapes various symptoms described here.
Some things are so bad that even in a “hot” topic like software development, there can be a consensus. I’m going to report some of the worst practices I’ve seen because I believe they’re quite common, especially with new technologies, the Internet, and open-source code. The larger the development team, the more frequent these small problems become. This post aims to report some experiences with scientific rigor similar to a bar conversation. All statements are relative and depend on the reader’s mood. Everything should be taken with several pinches of salt and humor. I warned you!
-
Over-engineering: Who has never seen a small example with 1000 lines of code doesn’t know what I’m talking about… it was supposed to be a “Hello World”… 1000 lines later, no one knew what it was. A computer program needs to be reasonably robust against failures, but there’s a big difference between a critical application and a simple one that can simply stop working and display an error message! The symptom appears when the reliability of the sun starts being put at risk. And the cruel question arises: When the sun explodes, what will we do? Variations of this reality include requirements like system web availability comparable to those of Facebook or Google, except you still don’t have their traffic or money. Variations include the desire to use the largest number of patterns in the greatest possible number of classes. Nothing can be done in just one class. Classes become better in dozens. Why leave it for a future refactoring what can already be done today?
-
Analysis paralysis: When programmers get lost in the maximum search: The framework quest. They lose themselves sometimes for months with experiments to simply choose a new framework, ignoring all previous experience of the team, or that lost time won’t come back. It’s very difficult to escape this situation, especially with open-source software constantly updated and improved. Stopping changing may mean death or condemnation to the maximum punishment: programming forever in Clipper or Cobol! The question is that no framework offers a solution for everything, or makes it simple, easy, and clear. Some people seek solutions that “don’t give trouble,” but forget that the fruit of this automation is usually following patterns and concepts from the new framework, which demands time to learn. A mix of analysis paralysis with over-engineering occurs when the team finds a solution perfectly scalable, adaptable to any client, and that can run for 10 years with little maintenance… or reaching the nirvana of programming.
-
Silver bullet: When the team decides to solve everything with the same technology always. Usually with C or Java, but more and more cases appear with script languages. The truth is that there’s no silver bullet (in the sense of universal solution), nor werewolf. Every solution must be carefully analyzed within a well-defined time frame, without falling into analysis paralysis.
-
Superstar Team: It’s every manager’s dream: A multi-platform team, multilingual, with highly intelligent, nice, and docile professionals. Other characteristics of this type of fantasy include using “The team” in any project, ignoring domain knowledge. Hospital, elections, rocket launch? No problem, “The team” solves it. Besides causing damage to the poor members of the team, the loss of productivity is enormous, leading to the appearance of the legendary Jack-of-all-trades-master-of-none. Nobody can dominate everything and achieve excellent results in all areas; that’s an illusion. Highly capable professionals may learn quickly and surpass the result of ordinary teams, but at a price. The dispersion of efforts and constant pressure can burn a development team very quickly.
-
Every man for himself: Many managers hinder, but when there’s no manager, everyone is their own boss! This is easy to fall into when you have a super-star or dream team in a particular technology. Since all are professional experts and experienced, complete independence is given to each member of the team. In the end, nobody understands anyone else because each member of the “team” has decided to use the practices and concepts of their favorite development current. The result is a salad of source code, probably written in various programming languages, with different frameworks, etc. A little order helps maintain the sanity of the team.
-
Precious Commits: Some teams are instructed to only commit their changes after finishing their implementation task. This works well and prevents code from being committed that’s not finished yet, which can disrupt other developers due to anomalies in the build. The problem is when some people don’t divide tasks into reasonable time frames. The symptom appears with commits every 15 days or even monthly! When asked about this practice, some may respond that they do it to avoid polluting the repository with small commits. The issue is that the developer needs a version control system for their own development. If a distributed version control system like Git or Mercurial is used, you can resolve these problems with local branches. With SVN, there’s not much to be done… you have to commit after testing and ensuring that what was committed doesn’t break the build.
-
Repository killer: When a member decides to commit and update only parts of the repository, depriving other team members of working with the same version of the code. This is inexplicable. A variation occurs when the developer chooses which files to update, forgetting to include new files and changes they themselves don’t remember making. This problem is a ticking time bomb because differences between repositories will be evident soon.
-
Local builds: Saving money on a build machine can be the worst economy in a software development team. If only one person develops the code, you’ll only know if everything was committed or not on the day they leave the company. Then comes the hunt for lost files. Another big problem is the lack of documentation of the development environment, but this issue appears more quickly, usually when the developer’s hard drive crashes and they spend a week to remember everything (IDEs, libraries, paths, etc.). This problem also reveals another, more serious one: the lack of testing.
-
Absence of tests: One of the causes of losing users is turning them into testers. To do this, you don’t develop automated tests and usually don’t perform any release control. This generates numerous problems throughout the development cycle, and the only test ends up being performed by the compiler. A code that compiles is far from being free of errors.
-
Zipmania: Developers stop using version control systems and start exchanging ZIP files with powerful names via IM: system20100610_XPO.zip. The transmission vector is a suicidal developer who decides to integrate on another developer’s machine before committing their changes, to “avoid conflicts.” It gets even better when they start creating ZIPs for “freezing” releases and making backups.
-
Two-character variable names: This is good; one day you’ll be punished, having to track code in C that uses strtok and variables like z, zz, and zzz! It’s more interesting when this code is responsible for unpredictable crashes, null pointer/segfaults. The worst part is that the executioner might have been you yourself! Always comment your code, especially when implementing something more complex than 10 lines. Even if you don’t comment it, use variable names that will still make sense in six months or two years.
-
Gotos: C code with gotos deserves no mercy; except for extremely rare cases, using gotos in C is unforgivable. It gets worse when the “labels” have the same name in various functions and the goto is hidden by macros. I learned programming in the 80s; gotos were the only escape in Basic, but they’ve been abominated in C and Pascal for a long time already (Dijkstra, 1968). For modern programmers, a goto is simply an attack on structured programming.
-
The Log Pest: Symptoms differ depending on the language. In C/C++, you can use the preprocessor to hide your debug logs or do it properly and use a decent framework. In Java, you have the loggers of life. The worst thing you can do is use multiple System.outs or printfs for manual logging. The Log Pest is that even when using a framework, you end up spreading logging lines everywhere and using only two or three levels of logging. As a result, you have enormous log files that slow down the execution of the program. I’ve looked for performance bottlenecks in Java systems and found that we had left the logs from C libraries enabled! Simply put, each line of the log opened, wrote, gave a flush, and closed the file in the same line, hidden behind a macro! Today’s C is so fast that you can do barbaric things, but when you log like this inside an XML parser… it explodes.
-
Ignoring compiler warnings: This is a prelude to suffering. If your code doesn’t compile with -Wall or if you’re the king of pragmas in Visual C++, remember that if these messages were really insignificant, they wouldn’t be generated by the compiler! In Java, use -Xlint for more precise warnings. The important thing is that your build should be clean! This way any error will be quickly noticed. The same applies to logs; if everything is shown on the screen, you simply stop looking because you get used to seeing junk or uninteresting messages.
-
Tabajara Localization: When working with code for multiple languages, it’s better not to improvise. Java does a great job of this, and Python has improved significantly in its version 3. Mixing strings across different pages of code is fatal! Using if statements in the code for localization is worse still. Even Microsoft tries to force the use of UNICODE in Visual C++. The worst part is ignoring differences in encoding and even message treatment. Besides word order, languages have different criteria for character ordering, plural forms, and uppercase letters.
-
Build scripts written in Perl and batches: Who still works with Perl deserves every punishment! The problem is people who don’t use Perl and try to write build scripts in it. The best part is when the builds are multi-platform and these scripts generate code in C or C#, without saying anything. To make things even better, you can use multiple scripts (multiples by the copy-and-paste rule, rename). Now the cherry on top is doing this all using Windows or Cygwin.
-
Babel: Why use just one programming language? Your project might have many languages, and you can let your developers choose the language that’s best suited to each task, then reunite it all via SOAP. Using Perl with anything else is counterproductive. C/C++ and Java are okay; a little Python is fine too. But if your code is written in parts in Basic, parts in C/C++/C#, and you decide to put the concurrent part in F# is because you’re living the .Net dream. The same happens in Java: Jython or JRuby, Groovy, and Scala. You can mix whatever you want, but maintaining the code will be a nightmare. The more languages you use, the harder it’ll be to find someone who can work with all of them. Remember that every language evolves, forcing an archaeological effort to understand what version X did with version Z. It’s better to avoid or ask permission from the High Council Klingon before starting to build your tower. And why just one tower? You can develop your code for multiple platforms, adding a bit of seasoning.
-
Demos: A demo is something for dogs! Who works with me hears this every day. The problem with “demos” is that they’re usually written to run only once and for a short time. What leads to the next problem. A demo is different from a prototype. A prototype is usually created to solve a specific problem or to demonstrate how analysts understood the problem, an instrument to refine and improve programs. A demo is usually created to demonstrate mastery of technology; it’s a PPT executable with code! Demos tend to outlive their usefulness and evolve over time.
-
Code written only to run once: Don’t believe this. Once a program is written, the universe conspires for it to be maintained for a long time. Almost never will you write code that runs only once. If so, make sure to delete it after the first execution just to ensure.
-
XML Doom: Someone decides that everything must be written in XML. I’ve even seen methods that receive their parameters in the magical format that starts with X. The most interesting thing about XML is that it was created to facilitate information exchange and also be readable by both computers and human beings. The idea was to have a simple format, based on UTF, or at least aware of the numerous problems of internationalization and representation of binary data in text format. Things started to go wrong when we began writing XML files manually for easier parsing of configuration files. The XML that should be generated by computers ended up being written by hand. Some are lucky enough to have adapted text editors and even tools that help with this arduous task. A good idea that was eventually abused. XML should not be used for everything.
-
Copy-and-Paste Hell: This is the worst of all. If you copy and paste the same code multiple times, something’s wrong. Or you’re not using your IDE’s template resources (those that transform syso into System.out.println()) or you’re really lost. Grave indeed is when you copy large parts of code, creating massive methods. Programs created with high participation in copy-and-paste demonstrate a grave problem of modularization and usually do not isolate concepts adequately. Remember that each copied and pasted code will have the same problems as the original code, if you’re lucky, making it necessary to propagate changes and modifications to all copies. It’s really a challenge for your memory. Always forget one or two… and there they are: the problems. Difficult indeed is correcting the same problem multiple times. Besides, the size of the program grows alarmingly quickly. After seeing code in JavaScript with 10,000 lines per file, you become afraid of this syndrome.
It’s a bit of a comedy, but all this is real. No profession is perfect or without difficulties; the difference in IT is that we create and remove obstacles on our path! Normally mixing new solutions with old problems at an incredible speed.