Software Engineering 101 for Front-end Developers
Every front-end developer must also be a software engineer.
About 18 months ago, I moved into a new role at Unscrambl, Inc. as a UX Designer, and the Lead for the front-end development team. Our talented team is building an enterprise web application of non-trivial complexity, and we’re actively looking for fresh blood to join the team of front-end developers (if you’re in the area, get in touch with me! 😊).
While interviewing several candidates for a front-end developer position, I spotted a troubling trend. Too often, the candidates lacked of knowledge and appreciation of good software engineering principles, despite possessing several years of experience with web development. In my view, every software developer in my team needs to be a software engineer first. The opposite of this is what I call a programmer who is not an engineer.
The syndrome understandably results from a variety of reasons, like
- When a developer has constantly been working on small and/or short-lived projects, and hasn’t had the opportunity to work on long-lived codebases.
- When the team’s culture has been emphasizing feature completeness and quick turnaround times over maintainability, test coverage, good code hygiene, and developer education.
- When the developer is fresh, and possibly self-taught without formal training in software engineering.
For any such developers who want to level-up their game, I’ve summarized what I consider to be some of the MOST important software engineering principles to be aware of and to abide by. I’ve followed it up with a short list of must-read books that will help you on this upgrade path.
Now I address you, the developers, directly.
The bottom line: with these principles, you will write better software. Your code will be of higher quality, more readable, less buggy, more testable, and more maintainable. You will never again be called a code monkey. Never again will you find that your code devolves into a messy spaghetti the moment it grows beyond a certain size or complexity.
So here they are: The Principles.
1. Don’t Repeat Yourself — DRY
This principle prescribes that you must not repeat any logic while writing code. So if you’re clustering two lists of objects based on a property on the object, write a method that does that, and call it twice. If you are validating a large number of forms on your UI, build a validation library (or pick an existing one), and use it everywhere. When you write code only once:
- You can expect that code to behave EXACTLY the same every time you reuse it.
- If you make a mistake or introduce a bug, it’s going to happen only in one place, and one fix will take care of it everywhere.
The guiding principle is that the less code you have, the less problems you have.
2. Single Responsibility Principle — SRP
We must admit that developers are also humans, and have limited mental capacity to handle complexity. Humans deal with complex ideas by reducing them into a set of smaller, simpler ideas. It reduces the total number of variables in the system, and makes it easier for one to wrap their brain around it.
That is the idea behind single responsibility principle. When writing code, you should write it in the form of small simple modules, each of which does takes care of a relatively simple and small responsibility. By module, I mean mean whatever is the unit of composition in your code — classes, functions, components, etc. So in real terms, SRP means writing smaller functions that do just one thing, smaller classes that have a small and singular responsibility, etc.
When modules are small and isolated:
- they are easy to conceptualize and implement for you. (easy to write)
- they are easier for your co-developers and future developers to read and understand. (easy to read)
- they are MUCH easier to unit test thoroughly. (easy to test)
- it is easier to add/modify their behavior, especially if you have good unit test coverage. (easy to maintain)
If done right, DRY will become a side-effect of SRP — you are automatically writing reusable code, and not repeating the same thing multiple times.
3. Dependency Injection — DI
This is, perhaps, the most strange and most important concept to grasp. Dependency Injection means that when a module depends on other modules to perform its responsibility, those dependencies must be provided to it externally. It must not retrieve those responsibilities by itself from a global scope, nor should it it instantiate that module itself (the “new” keyword is a BIG no-no!). In principle this sounds counter-intuitive, but in practice this prevents your code from becoming a big jumbled mess.
The benefit of DI is that when combined with SRP, not only do your modules become small, but also isolated from the rest of your code. And that is something very powerful.
- Your modules become reusable in a whole new way. (reusability)
- For unit testing, you can inject fake/mocked dependencies when instantiating your module in the test. (testability)
If you’re familiar with angular, or with Spring in Java, you may recall echoes of what I talked about above. Most modern frameworks will provide you a means to use DI in your code. The point of this article, however, is to introduce you to Dependency Injection as a fundamental principle of software engineering, rather than as a feature of an arbitrary framework. The frameworks that provide/support DI simply make it easy for you to follow this principle.
4. Unit testing
Unit testing is like flossing your teeth — we all should be doing it, but we often just don’t. I’m not going to tell you how to do unit testing here — instead, I’ll offer a different take on why we should do it. The most common reason offered for “why write unit tests” is that if someone inadvertently breaks the behavior of a module, failing unit tests will flag the bug. While that is true, my bigger reason for writing unit tests is this:
The act of writing unit tests will reveal to you if your code is well designed or not.
When your code follows DRY and SRP, it will be very easy to unit test. The anti-thesis of this is that if your code repeats itself over and over, you’ll find yourself writing tests for the same behavior repeatedly as well. If your modules do not have a small enough responsibility, you’ll find that writing unit tests for them will become harder. If your functions are too long, it will become near-impossible to test all the if-else logic branches.
The process of writing unit tests is amazingly good at raising all the red flags present in your code. If you find that writing unit tests is complicated and hard, it is a symptom that you need to re-examine your code’s design and refactor it.
So if you’re serious about writing well-designed and maintainable code, you MUST start writing unit tests, without excuses.
What’s Next — A Reading List
I don’t want to pretend that the principles I’ve listed above are the complete set of software engineering guidelines — it is not even close. Also, without concrete code examples it may understandably be harder to apply these principles in practice.
However, this is a good place to start off from. I highly recommend following this up with the following books on software engineering. These books do not market themselves as books specifically for front-end engineering, or backend application development, or any other specific area. But they do have plenty of code examples to help you internalize the principles and to let you Use the Force.
These books should be considered a mandatory primer for anyone who is serious about upgrading their software engineering chops. They will level-up you from a programmer to a software engineer.
- Working Effectively with Legacy Code, by Michael Feathers. This book will give you an appreciation of what it is like to work with long-lived code bases, and how to write code now so future You (and future Your Colleagues) can be happy developers.
- Refactoring: Improving the Design of Existing Code, by Martin Fowler. You’ll get a whole new appreciation for the word “refactoring” after reading this book.
- Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma et al. This is also famously knows as the Gang of Four book. If you want a simpler version of this, check out Head First Design Patterns too.
- Clean Code, by Robert C. Martin. Enough said.
- Advanced reading: Continuous Delivery, by Jez Humble and David Farley. This book talks about the software development process from an enterprise standpoint. This may not be directly applicable in small companies, but if you know this stuff, you can scale up your team without wandering astray.
Let me know of any other principles you think are Important (with a capital I), and about any books you think are equally deserving of being on a must-read list for software engineers. I’m glad to discuss it and add it to this writing.