Zen of Coding
Whether we develop Enterprise Applications, Machine Learning, Artificial Intelligence, Computer Vision or Mobile Applications, ultimately we have to engage in software development. The practices that we apply in developing and maintaining software are decisive in the quality and reliability of the end product. While the approach to development should certainly not be the same across different fields and corporate cultures, there are some common principles that are beneficial to follow.
Recently I came across an interesting collection (published by O’Reilly) of about 97 things every programmer should know. It is a worthy list but also a long list. It made me think about the Zen of Coding, a set of guidelines we have developed and applied at Reliancy.
We go to school and we take Computer Science classes. We learn about methods and principles. Then we end up doing Software Engineering. Software Engineering is not a science. It is an art form. Devoid of clear, provable principles, we are left with heuristics, guidelines and best practices. Always hopeful that they are effective and in some way true to the nature of Software Engineering. In that spirit I present to you a collection of wisdom we call the Zen of Coding.
1. Deploy Early, Deploy Often
You might be a good communicator. You might also be the best analyst. It is of limited use. Your client is the king and often they know want they want once they see it.
It is therefore imperative for the results to be seen ASAP and often. It will engage the customer more, but also, it will make your work more enjoyable. Seeing is believing and believing gives you wings. Literally.
2. Beware of Premature Optimization
Consider book publishing and in particular type setting systems. For example, let us take one to be MS Word and the other LaTex. Have you ever found yourself writing a report in MS Word and constantly jumping between editing and formatting?
In fact, if you ask the professional type setters what they use they will point you to a system similar to LaTex. However if you ever tried LaTex you will be shocked. It forces you to enter your paragraphs in plain text and then it does all the formatting for you. Well, not really, but jumping between formatting and editing is harder. You see the experts, by definition, use more efficient methods. In this case, to get the job done you first ride the keyboard, enter what you want to say and then and only then do you go back and format for pleasant view.
This approach is applicable to coding as well. If you dive immediately into fine-tuning your code, be it for speed, or elegance you will potentially waste time. Time that will be in short supply later. More importantly, you will discover that no one appreciates an improvement if none is needed. Remember, there is always more to be done. Why waste it on a micro task.
3. KISS your Code but Love It Not
KISS stands for Keep It Simple Stupid. This principle is also known as Ocam’s Razor. The best design is the simplest design. Ask yourself, if the solution does not require it why do YOU need it.
Closely related to this minimalist approach is the imperative to detach yourself emotionally from your code or technology. Why do you have to identify with a piece of technology. Invest your love in other human beings or maybe a puppy.
Simply put, you will be exposed to hardware and software platforms that are trendy and seem to be worthy a crusade. Just remember they are all a hammer looking for a nail. Use it or lose it.
4. There Can Only be One: If you Touch It, You Own It
This is more of an organizational principle. Sure you are a team player and sure everyone needs to pitch in. However every bit of source code, every image or icon, every anything needs to be attached to one and only one responsible person.
This clear association can and should be used to place blame or praise. If you do not believe in positive or negative reinforcement and blame is such a negative word then consider this: You are going to love accountability the first time you get sued.
So besides holding your team members accountable, the ownership principle makes the whole organization traceable and accountable. In short, it provides for transparency.
5. Code For Plugins, Avoid Inheritance
This is one of those indoctrination issues. Much like: ‘have an app need SQL’. You come out of school and half the time was spent explaining object oriented programming. It is the Alpha and Omega. They might have told you that inheritance is there for code reuse.
Now you start designing and coding and out comes a product with a hierarchy of classes. Great, wonderful, your prof would be proud of you. Or is it?. After hundreds of hours of refactoring and maintaining a class hierarchy we have concluded that class inheritance can cause serious headaches. Here is the essential issue. You define a class, methods and fields, you test all and you seal it. Now comes time to reuse. So you extend it. Looking back, to me, extending a class is akin to what Borg did to Captain Picard. Intrinsically intertwined I call it. Next thing you know some parent variables are needed but are not accessible. So you make them public or protected. Bang! You have destroyed all the time put into testing and sealing the initial class. You have introduces a pathway in your code that was not initially anticipated. Now you have to test the original class and the extending class. If you are brave and furious you will plow and test everything again. In the end you have a pyramid of dependencies. If you touch any bricks in the foundation the entire structure is destabilized.
Now consider plugins. Here you define a class or maybe extend a class minimally. You test out every process, every interaction. You anticipate extensibility and you leave slots for handlers or plugins. Your entire class is tested, calls to handlers fortified for exceptions and errors, and everything is locked and sealed. You know exactly what the class does and where you can intercept behavior. When the time comes to improve or extend functionality you take stock of all pluggable slots and you override initial functionality by installing plugins. You plugin might need more testing but you can be sure that your initial class will operate reliably. Now here is well composed class, one less class to worry about.
6. Design, Code, Test, SEAL
There is nothing novel in the event sequence presented here. Except if at all possible you should write test code before implementing the solution. Basically establishing a test suite as part of the design process seems most appropriate. What is of utmost importance is to remember the last step. Again after having spent hundreds of man hours on rewriting and refactoring you come to a solemn realization:
Never Change Sealed Code
If you were lucky to wrap up coding for a piece of software, and I mean here a reusable module, to test it and to declare it locked and sealed, never ever go back to add more to it.
Of course if the code is used it might have a bug and so you will have to fix it. But there is another force that will nudge you to reopen the code. If the code is used you will also be asked to add more features.Here you will have a choice: to change existing code or to fork off a copy. Way too often we are tempted to quickly modify existing branch, thereby nullifying all the testing. Why not make a full copy, and work on a new version.
Code reuse can be useful and tempting, but also a pitfall.
7. Review All and Review Often
Success can be a flaw too. In every (positive) growth you can find seeds of its destruction. In the world of designing, coding, thinking in general this seed is represented by thinking exclusively. When you start thinking that you are better, that you achieved only because of your merits and not because others helped, you are in trouble.
This attitude problem is not just a bad personality trait. It is not just corrosive for team work. It can lead to design and code that no one knows or understands. Code that is monopolized by one person, who thinks their work is superior and beyond reproach. The reality is that any code is going to have weaknesses. It is a solution to a problem and it either does one thing well or it does one too many. It is very important that both design and code gets peer reviewed. No one should get a free pass on this very important activity in the software life cycle.
If the design or code is worthy it will survive any challenge.
8. Thread Carefully
Sooner or later you will have to deal with parallel or threaded computing. In fact many things are best accomplished using parallel threads of execution.
Whatever you do, in whatever language you do it never ever underestimate the pitfalls. They are often cited but you start to appreciate for example a race condition only after debugging it for a whole day.
Your true and best friend in the world of parallelism is the immutable object.
9. Systems Facilitate, People Take Care
Lastly humans are not robots. And even robots would not be error free. Some situations in life are crystal clear and others are foggy. Consequentially everyone is bound to make a mistake.
Rules are there to be broken, right. During software development anything that can be taken out of human hands should be placed into a facility.
The propensity of humans to err, accidentally or willingly makes them risky and a failure point. For that reason you should have a system in place that facilitates (team) work. Let the team members take care of contributing not holding the fork on the road.
In concrete terms you can facilitate
- Source code versioning
- Documentation repository
- Issue Tracking
- Project and Task Management
- Persistent data storage
- Library of artwork and external libraries
- Coding guidelines and policies
…And anything else you can think of.
Software development is a rather challenging in nature. It should be fun, it should be enjoyable. We used the word Zen to emphasize that these guidelines are meant to help developers be at peace. We hope that by sharing our own experiences we can help others who might be facing the challenges of leading and maintaining software projects of their own.