visit
I have found myself falling into this trap more times than I’m willing to admit. Let’s create a ParentClass (that will be extended by only 1 ChildClass), let’s store the history of user status changes (even though we need only the latest one now), etc. Don’t!
After burning my fingers a couple of times, I found out I’m not the only one. There’s a principle called YAGNI:YAGNI- You Aren’t Going to Need ItMartin Fowler has ; the gist of it is:
We end up with three classes of presumptive features, and four kinds of costs that occur when you neglect yagni for them.
Now, when engineers or project managers argue we might need it in the future, I tell them YAGNI (and share the above link).
An example: We needed to show the job application status of a job. We thought, let’s store the history of the statuses, we might need it in the future. Needless to say, we didn’t. Moreover, we started seeing duplicate jobs in some cases. Due to a programming bug, the
INNER JOIN
SQL query was giving duplicates on production data. When I realized the root cause, I’m like I violated YAGNI 🤦♂️.In software, DRY is preferred over WET
DRY- Don’t Repeat Yourself
WET- We Enjoy Typing.
“Don’t Repeat Yourself” — How many times do you see that there are similar codes in different parts of a system? The DRY principle, formulated by Andrew Hunt and David Thomas in their book The Pragmatic Programmer, states that “every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” In other words, you must try to maintain the behavior of a functionality of the system in a single piece of code.
On the other hand, when the DRY principle is not followed, this is known as WET solutions, which stands for either Write Everything Twice or We Enjoy Typing.
—
Another way to look at this principle is to have a Single Source of Truth. I have another piece on the lengths I went to have a single source of truth. Totally worth the effort.
Recently, instead of copy-pasting a lot of raw SQL in my ORM, I created a small framework that takes in the params, and creates SQL queries based on that. That took longer than expected; because, you know, SQL. While writing the queries and fixing the SQL errors, I was regretting my decision. Nonetheless, I powered through. Weeks later, I had to add a simple
WHERE
clause in 7–8 complex SQL queries. Fastest change I have ever made. When the code worked in the first attempt, made me smile at the computer.Programs must be written for people to read, and only incidentally for machines to execute.
— Abelson & Sussman, Structure and Interpretation of Computer Programs
All the (premature) optimzations I have ever done have never ever solved (or partly helped) even one of the performance issues I have actually faced. Now, I solve for performance, when the profiler running in production tells me to, or when my apps are crashing with OutOfMemoryExceptions; not a moment before.
Make it correct,
make it clear,
make it concise,
make it fast.
In that order.
—
Don’t write
x << 1
instead of x * 2
. All modern compilers will generate the exact same output anyway.This always means:
Don’t commit commented code.
Delete commented code whenever you see it.Deleting directly unused code is easy. I take it a step further and delete old (unused) API endpoints and other functionality. Because that means I can delete the corresponding view, controller and serializer(s)! It’s not always easy to figure which endpoints are not being used anymore but it is always worth the effort.
My Favourite Code refactor so far!
By far my favourite story is : Engineers secretly hatched a plan to effectively deprecate IE 6, and secretly push this change without the necessary approvals from the management.This banner caused IE 6 usage to suddenly drop drastically!
These “Old YouTubers” first made the old code unused, then deleted the unused code! This story isn’t all about deleting old code. It just illustrates my point very well. Go to the extra mile to delete code.
The best way you can contribute to an open source project is to remove lines of code from it. We should endeavour to write code that a novice programmer can easily understand without explanation or that a maintainer can understand without significant time investment.
class User
List<User> users
get_user
and get_or_create_user
. Name your method as it behaves!This actually prevents me from violating the Single Responsibility Principle. If I need to name my class something of the form of
class DoesXAndY
I know I’m making a mistake.In my early open source days, I spent a lot of time renaming my variables after achieving the desired functionality. I used to think “What a waste of time. The code is working”. Now, when I read others’ code, I realize its importance. I don’t feel bad about being pedantic in code reviews: change
num_users
to number_of_users
or user_count
I needed to refactor the project’s Job model. It had a lot of related models. I guess the intent was to have a de-normalized schema. Instead, it was a tangled messed of bad data modelling. I knew how it should it look ideally. Naively, I created that schema and started writing migrations. It was like opening a can of worms. So many dependant things broke. It was stressful for me and the entire team. Instead of 3 days and 1 engineer, it took 7 days and 2 engineers. Not to mention the un-needed stress and loss of faith.
In hindsight, I should have removed one dependant model at a time, rather than all at one go. Remove one model, migrate it, test it. Then, repeat this process a couple of times.“Change One Thing at a Time” extends to the entire engineering process, not just to the coding phase. It’s very common for companies to break up their monolith into smaller services as they grow and handle more scale. This process takes months and years. Not only does the code needs to be refactored, the teams need to be re-organized. Project structure, deployment process, everything needs to change. All this cannot be done in one huge change.If you every wonder how to do a change that seems like moving a mountain, now you know how to do it. Break the task down into smaller bite-sized chunks, and finish them one by one.At Apna, we do feature sprints rather than the usual fixed-days (1 week or 2 week) sprint. We build one feature, test it internally, and then release it in production. There is no point in clubbing 2–3 unrelated features in a release. Our sprints range from 3 to 4working days long. This gives us the much needed speed as a startup, and reduces the amount of stress during a release.This is one of the most counter-intuitive thing I’ve learnt (rather, am learning).
The following excerpt explains it perfectly:If, like me, you were once a Junior Dev, you may remember your first experience looking at a Senior Dev’s code and thinking, “I can write that. Why aren’t I a senior?”
Yet I tried to write code like that for a long time, and I couldn’t.
What was so mystifying about “Senior Dev” code was not that I didn’t understand it, but that I could understand it immediately, it was fundamentally dumb, and it seemed like there had to be more to it. “Where’s the rest?” I remember thinking. “How does this do all of that?”
Since then I’ve learned all the names of all the principles and qualities of code that make it dumb: YAGNI, Single Responsibility Principle, DRY, Single Level of Abstraction Principle, low coupling, etc. And I’ve become a “Senior Dev” as well.
The greatest lessons I’ve learned are that writing dumb code is actually hard, and that it pays exponential dividends to do so.
— Why Senior Devs Write Dumb CodeKent Beck said it more provocatively:
Any fool can write code that a computer can understand.
Good programmers write code that humans can understand.
— Kent Beck
I have made the mistake myself of writing cool code like meta programming, but the real achievement would have been to get the same functionality without the “cool” code. In my defence, I did write a lot of comments.
Still, there are many more that I didn’t write down. I too forget to follow them sometimes. I do cut corners under time pressure. So, I wrote them down to remind myself. I have always regretted violating any of the principles a few weeks/months later. These are rules of thumb- you don’t have to follow them, but you’re always better off following them.
The principles mentioned above are not necessarily applicable to all of software engineering. It is more inclined towards high level software projects- python/java, web/mobile apps. The stuff that I work on. Probably most of it wouldn’t be applicable in, say, LINUX kernel development, the GNU C/C++ compiler, etc.I want this post to be a perpetual work in progress- add, re-arrange and remove ideas from time to time. Reply to this post or for any suggestions/corrections.