In the last few years I have been asked to help savor several web projects gone bad. The quality of the projects, code, environment, documentation, and morale has been low. To know what I think is right in situations like this, I started asking myself the question, "What would a responsible programmer do?".
Clarity
Above anything else a responsible programmer values clarity. Not only does she value clear code, but also clear documentation, clear communication and a clear vision of where she and her project is going.
Coding
Write Consistent Code
The responsible programmer writes consistent code. Consistency helps other
programmers read and understand her code. It lets them know what to expect. If
she names constants with SCREAMING_SNAKE_CASE
, they know that they wont
change. When naming attributes in CSS and HTML, she will make all of them
dash-er-ized
, or none of them. This is easy stuff, but important. Consistency
breeds familiarity. Familiarity is good, it removes worry and increases
confidence.
When the responsible programmer contributes code to other projects, she will make sure that she, consistently, follows the style of the project. Sometimes it is not easy to tell what style a project uses, the responsible way is to ask what style is preferred and then use that style. By just asking the question, she will often trigger a review of the code which will help set a consistent style in the future.
By writing consistent code, a responsible programmer will make the program easier to understand and easier to maintain.
Don't Quick-Fix
A responsible programmer doesn't do quick fixes. When a bug needs to be fixed she fixes the root problem instead of fixing the symptom. If an event-handler suddenly starts receiving unnamed events the proper fix is not to ignore unnamed events but, to figure out why unnamed events come at all when not expected. She knows that fixing a symptom will only make the root cause much harder to find.
Write Short Functions
Short functions are easier to understand, easier to reason about and easier to test. It is the responsible thing to write. Enough said!
Separate Commands From Queries
CQS or Command Query Separation has become all the rave in the DDD world, but it was coined by Bertrand Meyer in the book, Object-Oriented Software Construction in 1988. A good book, read it!
The responsible programmer separates her commands from her queries because she knows that they are easier to test and that she can call the queries many times without anything bad happening. Separating your commands form your queries may be as easy as:
// Can be called whenever, no side-effects function generateRoute(params) { return [params.major, params.minor, params.patch].join('/'); } // Updates the hash with the new route. function updateRoute(params) { location.hash = generateRoute(params); }
Refactor Mercilessly
Since a responsible programmer values clarity, she refactors mercilessly when her understanding of the system changes. She knows that the time invested in making the code a little more clear will prevent bugs and frustration in the future.
Prefer Explicit
A responsible programmer prefers explicit code over implicit code. Even though her understanding of advanced concepts such as meta-programming, monads and continuations is substantial, she prefers explicit code over beautiful abstractions. New programmers (including her future self) have a lot easier to understand code that is explicit than code that is not.
Don't Fear Advanced Techniques
Since advanced techniques can make the code a lot simpler in certain situations she never shies away from advanced techniques when she realizes that they are called for. At the end of the day meta-programming and "advanced" functional programming techniques are just tools that should be used when appropriate.
Check Boundaries
A responsible programmer always checks the boundaries of her system to make sure that invalid data doesn't enter into the core of the application. This way she can avoid defensive programming in the core domain where clarity is even more essential than anywhere else.
Wrap External Services
External services is one of the main reasons that development takes time, the responsible programmer makes sure to always wrap external services with a local interface. This simplifies both testing and exchanging the services.
External Libraries
A responsible programmer will never use an external library she doesn't trust. She will never add a library into her code base unless there is a significant reason for adding it. When she adds an external library she will learn it. She will learn how she configures it, how it is to be called, what are good practices for using it, what bugs there are, etc.
Balance
A code base can be compared to a balanced tree. A balanced tree is data-structure that will rebalance itself when new items are added to it. This makes the cost of modifying the tree more expensive but has the benefit that accessing items in the tree can be performed in an optimal way.
A responsible programmer treats her code base as a balanced tree. She will never add code without thinking about balance. She knows that if the project loses its balance it may have to be entirely rewritten to regain balance again.
The balance of the code may shift as the code matures, when a major shift is called for the responsible programmer will refactor mercilessly to obtain a new optimal balance.
Documentation
A responsible programmer writes and maintains documentation as the needs come up. The needs differ between projects, but most projects benefit from a system overview, a domain overview, a style guide, and code comments.
A responsible programmer makes assumptions all the time when coding, she writes the assumptions as comments in the code when she makes them. She tags them to make it possible to generate a list of assumptions.
The System Overview
The system overview is a drawing and a description of all the servers that are involved in the system. This includes databases, queues, web-servers, external services, etc. The description describes how the pieces fit together.
The Domain Overview
This is a drawing and a high level description of how the core domain of the system works. It includes the major concepts of the domain and what they mean.
The Style Guide
The style guide may be as easy as referring to Github's style guide or to write your own that alters someone else's. Anyway you do it, it is worth having it written down.
Code Comments
Comments in code should be very sparse and only added to point out idiosyncrasies. When assumptions are made, they can be written as comments with a tag to make it possible to generate a list. Example:
// ASSUMPTION: The list is expected to be small and will // be entirely loaded from the server function loadCities() { }
Testing
The responsible programmer tests! She doesn't test for the sake of testing or to increase code coverage, she tests to be sure that the code works as she expects it to.
She knows that dynamic environments such as Javascript and web browsers are fragile and that it is easy to break code without meaning to.
How to write good tests have been written about elsewhere and I wont spend any time on it here. I can recommend the last chapter in Sandi Metz' book, Practical Object-Oriented Design in Ruby if you are interested in good techniques for testing in dynamic programming languages.
Environments
The responsible programmer owns her environments. In a project there are at least three environments to care about, Production, Test, and Development. There is also the development machine itself.
The responsible programmer can set up all project environments with a single command that installs everything that is needed, including databases, seed data, libraries, search engines, tools, environment variables, SSH-keys. Everything!
This will allow a new programmer starting in the project to be setup within minutes. It will also allow her to experiment with anything without having to worry about destroying the setup and losing days debugging the environment.
Her personal development machine is also perfectly configured at all times. If she learns a new trick, she will immediately incorporate it into her toolset and into her configuration.
She is automatically prepared for catastrophes. If her hard disk crashes she can just buy a new one at the local supermarket and install her configuration files and be ready to go within hours.
To make this happen she always keeps backups of her configuration files. She keeps the non-secret ones on Github and the secret ones, such as SSH keys and passwords elsewhere.
Continuous Integration/Deployment
Another part of the environment is continuous integration. If a project doesn't use continuous integration it is a clear sign that it is not healthy.
The continuous integration server is just another environment and setting up a new is done with a single command just like the others.
The responsible programmer will set up and maintain continuous integration just like she does every other environment.
Scripting
In order to achieve the environment goals, a responsible programmer knows how to script. Scripting is not only essential for keeping your environments up to date, they are essential for automating simple tasks. Scripts are useful for generating code, testing, refactoring, renaming, installation, automating checklists, etc.
A responsible programmer ask herself, "When did I do something once? Never!" Writing a script that does what she wants frees her from having to remember the sequence of instructions required to do a task and let's her focus on more important stuff. It also serves as runnable documentation.
Tools
A responsible programmer knows her tools. She will always try to learn more about them and she will replace them if other tools are invented that work better. But she doesn't change her tools for the latest fashion.
The command line is a very powerful tool and so is scripting language and a scriptable editor.
Version Control
The responsible programmer uses version control to communicate with her future self and with other programmers. She knows that a clear commit message will help her and others understand what has happened to the system.
She prunes her commits. When she has made several changes to a code base she
will make sure that she commits the different changes separately by using
something like git add --patch
. She also knows that if she commits something
by mistake she can alter the commit message or add forgotten files with git
commit --amend
and that she can change the contents of her history with git
rebase --interactive
or with git reset
Projects
Own It
A responsible programmer owns her project, she will not allow anything bad to happen to her code. This is a difficult goal to achieve when she comes into a project that has already gone bad, but it is a worthy goal and not one that should be taken lightly. All projects must have at least one person who owns the code. When people start talking about the code as if it is someone else's, it is time to shut the project down.
If a responsible programmer decides to take ownership of a project, she makes sure that she has the authority to make the decisions that she deems necessary. No authority, no responsibility, it's as simple as that.
Estimation
Sometimes projects require estimates, most of the time they don't but, sometimes they actually do. A responsible programmer knows how to estimate. She knows that an estimate is just a guess and the bounds of any task, however trivial, always have a small probability of taking infinitely long to finish (earth quake, meteor strike, blackout). There is also a small probability that the code is already being written at the time of estimation.
Being aware that the code may take infinitely long to finish she is very careful not to make any promises and she is very clear about her estimates being guesses.
Don't Do as They Are Told
Some people may not see this as a sign of a responsible programmer but I beg to differ. When a programmer is told to do something, she will try to figure out what the real problem is. She may do this in several different ways. She may sit down and think through the "task" and come up with an alternate solution that solves the problem simpler or, even better, makes the problem go a way completely. She may ask questions to help clarify the problem for her. Why is this a problem? Why do you do it like this? Why? Why? Why?
Some people may not like this and tell her to "Just fucking do it!". Her reply to this is something along the lines of "Just fucking do it yourself!" but she is usually a lot more polite so she may very well say "I don't understand what your problem is and, therefore, I am not the best person to solve it, please ask someone else."
She believes it is her job to understand what she is doing and why, and that life is too short to be a drone!
If she feels that she is not able to be a responsible programmer on a project, the responsible thing to do is to leave.
Summary
I have found that asking myself the question "What would a responsible programmer do?" liberating. It clarifies what I should do in situations of doubt.
At the end of the day, the responsible programmer can look through the commit
log and see a beautiful list of tasks that she has completed. She can look
through each commit and see that they are cohesive and well described by their
commit message. She can git blame
the code and see that every line of code
that has her name on it reads well. She can look at her days work, and she can
feel proud!
Excellent post. Good to know that some still believe in excellence.
ReplyDelete@sandro, I'm glad you liked it. If you like excellence you are going to love the book Mastery, which I write about here: Mastery
ReplyDeleteWhat a nice post! Gonna show this to all programmers in my company
ReplyDelete@anonymous, I'm glad to hear it.
ReplyDeleteWhy programmer is "she"? =)
ReplyDelete@Vitaly, Why not? :)
ReplyDelete@Anders Just kidding. It's unusual though. )
ReplyDeleteGreat article.
@Vitaly, thanks, I thought that it would be interesting to use a she as the hero of the story!
ReplyDelete"refactor mercilessly" that's just wrong. Too much refactoring is a bad thing. Not for readability but for consistency. You have expectations that an API exists and that it works a specific way. (no protected or private code here). Refactoring is supposed to maintain the method signatures as-is but in practice that'snot the case.
ReplyDelete@Richard, I disagree with you about refactoring, I think it helps make a system consistent. I agree it is a different matter once the API is public, though, then you have to start doing versioning etc., but internal APIs I think should be refactored until they are consistent with the rest of the application.
ReplyDeleteI need to print your post and stick to the wall in my office :-) Thanks for the excellent post!
ReplyDeleteit was awesome to see "she" all the way through this. Great list.
ReplyDelete@Jakub, You're welcome, I'm glad you liked it!
ReplyDelete@anonymous, I'm glad you liked it.
ReplyDeleteExcellent Article !!
ReplyDeleteWRT Refactoring:
I would prefer 'Refactor appropriately'. Firstly, there are always reasons to refactor code, but you need to stop somewhere :) A typical example is performance; sure you can screw another 2 cycles out of the initialisation routine, but is that the best use of your time ?
hth
@Martin, Thanks!
ReplyDeleteI can't argue with appropriately. It's not a very good argument to say "Don't refactor appropriately!" ;)
I view refactoring mostly from a readability perspective and I feel that many people don't do the little extra that is needed for a maintainable code base.
While I agree with most points, I simply can't agree with your views on continuous integration.
ReplyDeleteIs CI a good thing? Sure.
Is a lack of CI "a clear sign that [the project] is not healthy"? Absolutely not. That's religious talk there. I have worked as a professional (and responsible) programmer for a long time, and CI is only another tool, definitely not necessary for the long term health of a project.
@anonymous, If I come into a project and it does not use CI, I see it is a sign that the project is not healthy.
ReplyDeleteI have seen few that don't use it and are still in good shape. And, I have seen many that use CI, but are in bad shape.
But, not using CI, when it is so easy to setup is, I believe, irresponsible.
Well, my experience differs pretty drastically from yours. And we'll just have to agree to disagree on this point.
ReplyDelete@anonymous, sure, experiences differ, I'm just glad that we agree on the rest :)
ReplyDelete@anonymous - If I find a project where CI is not used, I check out how easy it is to deploy. There's a better than evens chance that you won't be able to check out and get it running with a single command as suggested in the article. Just setting up CI forces you to think about these things.
ReplyDeleteThis is a great summary! Couldn't agree more. I'm sharing it man =D
ReplyDelete@Gustavo, thanks, I'm glad you liked it.
ReplyDeletethis is a nice post because i still believe in exellent. http//wwww.unn.edu.ng
ReplyDelete@nna, I'm glad you liked it :)
ReplyDeleteExcellent post guys
ReplyDeleteWeb Designer in Bangalore
Great post. I'm not sure if I agree with your statement regarding refactoring and not doing as they were told though. Ideally you are right. But a responsible programmer also knows how to stay within boundaries: either financial or time given. Refactoring takes time and thus costs money. Both may be an issue. What's the value of code that is refactored to the max making it state of the art, but didn't finish on time? Stakeholder may cancel the project, making the code void, regardless the state of it. The same may happen when the project goes over budget...
ReplyDeleteRefactoring? Absolutely, I do it often. But it should serve the project and not be a goal on its own. Not doing as was told? My opinion is that a stakeholder, product owner or whoever should describe a desired end situation, but leave the developer free on his/her choice on how to get there.
@anonymous, I see what you mean, but more often than not, we, the responsible programmers, are the best people to make that judgement. We can't expect the stakeholders to know the cost of the refactoring and if we tell the stakeholder that this is necessary and must be done right now and then are not allowed, we are essentially saying that it is not our responsibility anymore. I don't think that is the responsible thing to do.
ReplyDeleteIf we are pressed for time, we can still do small refactorings as part of our regular work.