How to conquer your fear by writing production-quality software
Does the thought of making changes to your custom software application fill you with dread? Does your heart start to race every time the phone rings or the boss walks into your office? Do you spend nights and weekends fixing the problems in your application with Band-Aids just to keep it limping along? Does your day mostly consist of tracking down problems and fixing data directly in the database just to keep the business running or the customer less angry?
If you answered “Yes” to any of these questions, don’t feel bad. We’ve all been there. A little fear can be a good thing, especially if it makes you a better developer. But you might ask yourself, “How did I get here? Where did it all go wrong?” To answer those questions, you first have to identify the problem.
The Problem
“Yeah, yeah, I know the problem. Just skip to the solution,” you say. Well, maybe you do, but there may be things going on that you didn’t even realize were problems because you were so busy fighting fires that you never even noticed them. Here are some of symptoms that your application has gotten the best of you:
- You never have enough time to fix problems in the code because you are always troubleshooting and cleaning up the mess caused by those problems.
- Every time you do make a change to fix something, you break three other things.
- Problems take forever to track down because they are difficult to reproduce and you never got around to adding logging like you thought you would.
- Nobody knows how the application is supposed to work because there was never enough time to design it and especially not to put that design into writing!
- All code changes you make go straight into production because who has time to test when there are so many other fires to put out?
- Your boss keeps asking you why you haven’t completed that new feature that was due a week ago. You don’t see her working a 70-hour week.
- When you are asked to provide an estimate for new work, you have to quadruple the time because you are so afraid of breaking other things. Then you triple that estimate just to be safe.
- The users complain about how slow the application is. Can’t they just be grateful it is working at all?
- The minimum-wage intern your boss just hired “to help you” accidentally deleted a major order that must go out today. Perhaps you should have considered a security module for that internal application after all!
“Okay, so maybe some of those sound familiar. What is the solution?” you ask. Not so fast. You now understand the problems that can arise from poorly designed and coded software, but you need to know why those things happened first before you can learn how to avoid them.
The Cause
There are many causes to poorly written software, and you are not always to blame. Except you usually are because it is your job to make sure things are done right. But even when things are at their worst and you find yourself daydreaming about having a job where you actually had time to update your resume, take comfort in the fact that you are receiving the best training in how to be a good developer that you will ever get. There is no better teacher (or punishment) than having to maintain your own piece of crap software, especially when you are still working on it a decade later. I promise that you will think twice before making these common mistakes again:
1. You did not understand the business problem you were trying to solve
This is an easy trap to fall into because as developers, we all want to jump right into coding. We also have the business people telling us exactly what they want us to do to solve their problem, so it is easy to absolve ourselves of any responsibility. But without questioning the proposed solution and finding out what the true problem is, you risk creating an application that doesn’t help anyone.
2. You skipped the design phase or failed to document the design
This is another thing that developers love to skip because who likes writing documents and creating mockups when there is interesting coding to be done? But someday soon, the customer will ask why the application works the way it does instead of the “correct way” and you will have no proof to give them that they are the ones that told you to do it that way in the first place. Or, you will go back to fix a bug or add a new feature years later and not remember how it was supposed to work.
3. You chose to code using the coolest new developer toy instead of the proven tool
Developers love to learn new things and try out the newest tools. There is nothing wrong with this, and it can be a great way to keep up with the latest technology. But if you always insist on using your newfound knowledge on your projects just because you are tired of the old way, you are setting yourself up for a missed estimate and a maintenance nightmare. As the old saying goes, “If all you have is a hammer, everything looks like a nail.” In other words, you will force the project to fit your tool instead of choosing the right tool for the project.
4. You only coded the “Happy Path”
Every developer starts out as a doe-eyed optimist. We believe we only need to code what we are told to, and everything will work out great because nothing will ever go wrong. This is called the “Happy Path.” But you know how it ends: We eventually all get burned and turn into easily-spooked pessimists that can’t write a single line of code without considering every possible way that line could come back and bite us in the butt. And that‘s a good thing, because it will. That line is probably plotting against you right now.
5. You forgot about security until it was too late
Let’s face it. Security in an application is not glamorous. For most of us, it is a necessary evil. But it is also absolutely critical and best built (and tested!) early in your application framework. Security comes in many flavors and at many different levels which sometimes makes it hard to determine what is truly needed for a given application. The easiest way to figure this out is to consider one simple scenario: what would happen if someone gained unauthorized access to your application? Do you have logging and/or audit tables to figure out what data was exposed? Did you encrypt sensitive data in the database to make it harder for the hacker to use it? A good security model can take a lot of time and money to implement, but one foiled hack attempt can make it all worthwhile.
6. You failed to add good comments to the code
Admit it. At some point in the past, you have complained about another developer’s lack of good comments when you couldn’t figure out what their spaghetti code was trying to do. But then you later skipped commenting your own code because “it is self-documenting.” You rationalize your decision because you think your code is readable and easily understandable by anyone with half a brain. Well, it’s not. I don’t care how good you are; there are large portions of your code that need explaining. Most good developers can probably determine what you are doing in the code. The critical question is why are you doing it? If you don’t comment the reasons behind your choices, even you yourself could eventually fall prey to it when you have to troubleshoot a problem five years later.
7. You chose complexity over simplicity to make your job more interesting
We have all seen this, and most of us have been guilty of it at some point in our careers. You over-engineer your code and swear up and down it is absolutely necessary to make it “future-proof.” But the real reason you do it is because you are bored coding the same thing over and over again and want to try something new. So you structure your code in layer upon abstract layer. You start to use multiple inheritance and interfaces everywhere. You store all the user interface code in the database so screens can be built dynamically because, “why not?” You brilliantly diagram on a napkin over beers with a friend that any database schema can be designed using only four tables, and you know you just have to try it! But then your application goes live, and that is when your Frankenstein software reveals its inner monster. Suddenly you have performance problems everywhere, you are the only developer capable of making even a simple change in the code without completely breaking it, and each of those changes takes forever to implement and test. If you are lucky, the users will come after you with torches and pitchforks and put you out of your misery. If not, guess who gets to support that application for the rest of their career?
“Enough! I get it! I suck as a developer and there is no hope for my future. Is there anything I can do before it’s too late?” you say. I’m glad you asked….
The Solution
Luckily for you, all of the above problems are easily avoided with a little foresight and self-discipline. These tips may only help you with new applications, but being forced to maintain that terrible code while you build new applications should provide all the incentive you need to do it right this time. To that end, I present the following guidelines on creating production-quality software:
1. Understand the Business Problem
Always conduct proper business analysis on the problem before writing any code. If you understand the problem you are being asked to solve, you will recognize immediately when the
customer proposes only a partial solution or one that won’t work at all. It also helps to design and discuss your proposed solutions with the actual end users throughout the design process. They always have the best feedback since they are the ones working with the application on a daily basis. Also, don’t underestimate the power of a good mockup. Often, users will not think about a missing requirement or come up with a great idea until they can visualize the final product.
2. Choose the best tools for the job
Use the following hierarchy to determine the best coding tools and framework for your application. Select these based on:
1. The customer’s requirements
If the customer is asking for an application with normal relational data, don’t choose a NoSQL database just because you want to learn more about it. The opposite is also true: Don’t simply select a tool because you are familiar with it. Research the best (and simplest!) solution to the problem before selecting the tool.
2. The deadline and budget
If you have lots of buffer built into the project timeline and budget, feel free to experiment with a few new tools, taking into account #1 of course. But if you have a tight deadline and/or budget, it is always best to stick to the familiar whenever possible. This will greatly reduce the risk of going over budget or delivering late.
3. Your expertise
This ties in closely with both #1 and #2. Sometimes the project requirements dictate learning a whole new suite of tools, but often you are developing something very similar to something you have done before. In that case, it is usually best to stick with what you know for the majority of your solution. I find a good guideline is to select no more than one or two new-to-you technologies per project. This gives you a chance to learn something new without greatly increasing risk.
4. The expertise of your colleagues
Take advantage of your coworkers’ knowledge when selecting tools for an application. They might have already developed something similar that could help you. Keep in mind that if you choose completely new technology, there may be nobody to help you when you have problems. You will also find that you are the only one capable of maintaining that product as well.
3. Design a good security model
Security requirements should be collected during the initial phase of the application, before coding begins. Since security varies so widely from application to application, I only offer here some suggestions of topics that should be considered while gathering the requirements:
- Will the application be only for internal users, external users, or a combination of both?
- Will internal users come from Active Directory or some other user repository?
- Do you need a User management screen in the application?
- Do you need a login screen?
- Will there be more than 1 or 2 different roles in the application, each with different rights? If so, consider a permission-based system that allows the user to manage their own groups and assign these permissions as they see fit. That will prevent the scenario where 3 roles turn into 12 roles and your code becomes a never-ending chain of IF-ELSE statements.
- Are you storing any personally-identifiable information that should be kept secure? Remember, always encrypt social security numbers, credit card information, and passwords (the latter using a one-way only, salted encryption). There is really no excuse for not doing this when creating new applications today.
- Do you need to log access by users to parts of the application for auditing purposes?
- If you are allowing external users to access the application, be sure to consider each of the following features:
- Ability to request access
- Forgot password functionality
- Password security features like minimum strength requirements, automatic expiration, prevention of password re-use, and automatic lockouts on too many failed login attempts.
- Auditing of all failed and successful login attempts
4. Build a core framework that handles errors well
Laying down a good application framework from the beginning prevents many headaches later. The first things I add to an application when building one from scratch are:
- A logging solution that can be used throughout the application. And, make sure you actually use it! Write log statements while coding that will help you troubleshoot problems later. Include any relevant data to help locate issues more quickly.
- An exception-handling plan. Everyone has their own preferences and style for handling exceptions, but most people would agree that you never want your end users to see the raw error. That should be logged, but never shown. Intentionally create errors in your application to see how it handles each situation to determine if you are handling all the possibilities.
- Email notification of all errors. With good exception-handling and logging, your application may be responding to errors correctly, but how will you ever know about them if the end users don’t tell you? I like to have all exceptions, including those that happen client-side in a web application, emailed to me automatically so I can know about problems as soon as the user does.
5. Evaluate performance early and often
I once knew a developer that was building a grid component for a web application from scratch. He was very proud of the way it did all of the sorting, filtering, and paging directly in the browser because it was “so fast” and “never had to call back to the server!” Unfortunately for him, the application needed to be able to handle millions of records in the underlying tables. And, of course, he never actually tested with that many records in his database. Needless to say, performance was abysmal (when the web server itself wasn’t crashing). Don’t be that guy. Only load from the server the data you actually need. And, test with lots of records as early in the development cycle as possible. This will prevent a lot of those difficult-to-fix performance issues later.
6. Build for simplicity and maintainability
The simplest solution that solves the problem and meets the requirements is always the best solution. There is no reason to add complexity unless the problem demands a complex solution. So keep the code simple and easy to understand wherever possible. It is also helpful to create a modular application framework that separates code for various business modules into their own classes or libraries to minimize the impact to other modules when changes are made. Code that is shared across modules can be added to a common library which is referenced by each module.
7. Comment your code the right way
A good comment explains why you are doing what you are doing. You don’t have to add a comment for every line, though. Even a brief description at the top of a class or function that describes its purpose is good enough. But it is absolutely critical that you explain the purpose of any workarounds and non-standard ways of doing things. There are many times I have had to add features to code I wrote many years ago, and having those good comments in place made it so much easier to avoid breaking the existing code, especially when I wouldn’t have remembered why I did something. I also strongly suggest that when you make changes to code after the application is in production, always add comments that include your name, the date, and a brief description of the change you are making and why. This is very helpful in getting a clear picture of the chronology of changes, like when a later change undoes a previous one.
8. Document your design
I can’t stress how important this is for a custom application. I have seen too many cases where the only way to answer questions about how the software works is to study the code. And if the code is bad and there aren’t any comments, you can come away more baffled than when you started. The answer to this is a good design document. A design document should reveal exactly how the application is expected to work. Every screen, every field, every button should be documented. Automated processes, reports, security, all of it should be in there. This is critical if the application is to be tested thoroughly (you do have quality assurance, right?). Otherwise, the testers are just testing blind and hoping they find all the problems. User documentation and online help can also be created from the design document. But mostly it is for you, the developer. One year, five years, or even ten years from now, you will not remember how the software is supposed to work without this document. So keep it up to date and it will pay you back ten-fold!
9. Test your own code
Seriously, test your code. I don’t mean just a couple of scenarios; I mean every scenario. “But that is what QA is for!” you whine. No, they are your backup. Find your own problems and fix them first before handing your slop over to someone else. Don’t worry, you’ll still have plenty of bugs to fix. But, by the time you say a feature is done and check it in, you should feel comfortable enough to put it directly in production (please don’t). Oh, and don’t forget to update the design document. Your work isn’t finished until you’ve done that.
Now that you have read through all the guidelines, you are ready to conquer your fear for good. Don’t ever let another application get the best of you. Design it, code it, comment it, test it, and document it the way you know you should. Constantly ask yourself if the code you are writing is production-quality.
If you are ever forced to answer “No” read through these guidelines again. Then go back and fix up your code until it is something that makes you proud to call your own. Or just go into management. Your call.
–By Jon Hester, Senior Software Developer and Architect at Kopis
Jon Hester is a senior software engineer and architect at Kopis. He has been with the company for over 10 years and specializes in business analysis, user interface design, and complex problem solving. In his spare time, he enjoys computer gaming, reading, activities with his kids, and playing practical jokes on his coworkers.