Introduction
You might've heard the term production-ready code, in conference talks you watch on YouTube or maybe from senior developers. I certainly remember hearing Uncle Bob say "(...)You know how to ship good code" (in the timestamp 24:30 of this talk), and he goes on and says: "(...)you know what good code looks like (...)" (in the timestamp 25:20 of this talk). I think I remember him saying something along the lines of "(...)We all know what good software looks like, so why don't we do it?", although I couldn't get a reference for that one.
In reality, I didn't really know everything I should and was expected to, and I don't think being a junior and had only done college projects is a good enough excuse. Maybe I built 1 project that is like 50% the size and complexity of a real-world application for a business.
I think developers typically mean to say: "you need to have error-handling; concurrency in mind; if failure happens don't avoid errors with empty catch clauses 😅". Error handling isn't quite trivial for me, it was at least a topic that I would ask the teacher of the class "how should we do this? What is the best approach". But all of this matters because this code will go to the production environment, which is accessible by users.
Good clean code, as of my understanding, is about certain properties that leads to production-ready code. The code that we are confident will work for our users and clients. We'll talk a bit about some of them next.
Readability 📃
It's very helpful to have a set of conventions established in your codebase, for example naming functions, files, identing. These are all aspects that help to an end goal, which is readability. At my early stages, I didn't really follow a naming rule for my event handlers in React. But it's something that truly helps when a colleague that didn't write the code, reads it and is able to know what a function is doing and in what context. Why? Because my colleague may be the one who needs to change a piece of code that I wrote, so he needs to understand it. I know it takes an extra effort to not just make the code work, but also make it readable to others.
Defining interfaces is also crucial for the teams's success. Say you have this code snippet:
const records [{id: 1, date: '02/01/2020'}, {id: 1, date: '01/01/2019'}]
Now you need to make a change to remove the records in the past. You might make this first change:
const records = [{id: 1, date: '02/01/2020'}, {id: 1, date: '01/01/2019'}]
const filteredRecords = records.filter(r => !recordIsInThePast(r))
function recordIsInThePast(record: {id: number, date: string}) {
return moment(record.date).isBefore(moment(), 'day')
}
But now the function recordIsInThePast
is harder to be used in another scenario, because to call it you need a record object (imagine the object having more fields). You should strive to have the least possible knowledge in a software component to make it modular and re-usable. In this small example, the function only needs to know of a date, because it doesn't use any other fields from the record object:
const filteredRecords = records.filter(r => !dateInThePast(r.date))
function dateInThePast(date: string) {
return moment(date).isBefore(moment(), 'day')
}
This might seem simple and easy to do, but for me at least I find it difficult at times because I just get caught up with the current problem I'm solving and the domain I'm working in. If we can abstract ourselves from our current situation, we are able to better design our interfaces in the code.
Sidenote: How to measure code quality (WTFs/minute)
I recently watched this talk and I like the WTFs/Minute measurement so much, I googled that image, printed it and stuck it on my wall 😂. That might be a silly thing to do, but I like having some pictures above my computer, so that when I'm coding on my desk and I stop to think, sometimes I'll look to some of the pictures to remember stuff.
Error-handling 😵
This is an important manner because when we are talking about a production environment, it means it's the code that executes for real users. People using the software you wrote... that will break, because they did some action or set of actions that you didn't prepare your code to handle. This leads to bugs being filled and devs to pushing hot fixes. This is very much related to code maintainability because you might not be the dev that will change the code that is causing the bug. And so the code you wrote can slow down the person trying to fix it.
One thing you can consider in some error scenarios is to have the ability to recover or retry the operation that failed. This is useful if your application, for example, interfaces with a third-party API.
I've learned that logging what happened is very important when trying to figure out what happened wrong. Now, I'm not saying we should log everything, but this is something we have to think about when coding, because when an error occurs in production and you don't have enough insight to know what happened wrong, it's harder to fix. One tool that can help you with logging is Papertrail. The feature I like the most is the ability to set up webhooks and get an alert when something happened. You can integrate with Slack and get notified when the string "ERROR" happens too many times, which is pretty cool.
Maintainability ⛏
This property can vary in importance because it depends on the lifetime of the project you are working on. This is usually a top priority when the software is going to run for several years, with the need to evolve or change. But also in cases that there isn't as much of a need to add new features, but simply to fix errors as they come up. These systems can have a lot of legacy code that you need to read, understand and navigate through.
When you need to modify existing software that you didn't write, one thing that helps you the most (in my opinion) is navigability of the code. Can you find the code that you are looking for easily? Are related files next to each other?
One tip I'd give is to try writing the reasons for your code changes in the git commit messages, in order to better document your decisions and though process. With tools like GitLens you can give more context for the person reading your code that might be questioning: "Why is this here?!".
Documentation in itself is something very valuable when releasing software, for example, it helps new contributors get started with an open-source project or a public API. If the API requires authentication, then document how a user could get an API key or an OAuth token, depending on your solution.
Testability ✔️
Developing tests might be hard and time-consuming (especially integration and end-to-end tests). But by putting yourself in the QA's shoes (Quality Assurance), you can often catch some errors and build tests that make sure they are fixed and don't surface again.
For example, say you have a form on your web page that lets the user change their username and their bio. But there is a functional requirement that says: "if there are unsaved changes on the edit form, when the user tries to navigate to another page a popup appears, asking for confirmation to discard those changes or keep editing". You go ahead and implement a NavigationGuard
component so that this functionality can be used anywhere in your application that provides editing capabilities for the user.
We could write a test for that component that asserts the modal doesn't open when the user successfully saves the changes, for example. This would help prevent any user from possibly editing some information, clicking "save" and then when they navigate to some other route on the web application, the popup still shows up, confusing the user.
If anything, tests may help new developers coming to a new code base, since they can go to the tests and read what a function/module is supposed to do. They also give developers confidence regarding their software and provides a cushion when making changes. Refactoring code is safer when we have a test suite that covers the use cases the component/module is being used for.
Conclusion 😃
Learning to write production-ready code doesn't happen overnight. I still make mistakes, but I keep learning and searching for knowledge from more senior people. Always try to have your code reviewed by someone else. If you are self-taught, join some online communities and ask questions there. Go on Reddit, join Discord servers, try to network on conferences (kinda harder nowadays 😅).
I hope you enjoyed reading 😄. There are a few other properties I didn't really mention like scalability or extensibility. So I'd love to hear people's opinions on this topic, since there are obvious standards in the software industry, and there is a certain level of quality expected from developers that some juniors might not be aware of when getting their first job.
Additional Resources 📚
Here are some talks and written material about this topic that you might want to watch/read to learn more:
Top comments (0)