It's another year to be a better developer. If like me, you are set out to be a better developer than last year, here are some software principles to apply that would improve your software development cycle.
Don't bother about that! You're Aren't Gonna Need It (YAGNI). That's a practice to imbibe when developing software. Add only required features, not features you think will be needed soon. Ron Jeffries one of the three founders of extreme programming states it in this way. "Always implement things when you need them, never when you just foresee that you need them". That's the whole essence of Agile development. Create the required features, the users' feedback to a product will tell what additional features to add. Then iterate. It saves you the following costs:-
- Cost of building that additional features which might not have been necessary
- Cost of delaying the release of the product because you want to add an extra feature that you're not going to need.
- Cost of carrying the feature as each feature added adds additional complexity to the codebase and therefore increases the cost in terms of time spent debugging
- The cost of refactoring the feature when you find out the user does not interact with the feature the way you had imagined they would.
- Cost of repairing to build the right features or right way of interacting with a feature based on user feedback.
What's a stupid codebase? A STUPID codebase is one that uses:-
- Singletons - Most times we need more than one instance of a class. Learn more about the dangers of singletons here
- Tight coupling - Why link two components in such a way that changing one will not work unless the other is changed?
- Untestable code - Shipping without writing tests is a recipe for broken deployments. Difficulty in writing unit tests shows that your code is tightly coupled. Integrations test, snapshot testing, end-to-end testing all have their use cases and should be written as necessary.
- Premature Optimisation - Don't waste your time trying to optimize parts of the codes that do not improve the overall software performance.
- Indescriptive Naming - Naming can be a pain sometimes. However, it is more painful to give names that do not describe well what the class, function, method, string e.t.c does. After some time when you or another developer visits the codebase, you would be asking the question 'What does this variable do?'. Some principles on naming variables can be found here
- Duplication - Copy and paste may seem easy but truly it makes the code ugly. You have a long codebase full of redundant codes. Learn to make it DRY
Don't Repeat Yourself(DRY) is a basic principle in software development. The aim as the name implies is to save time in development by avoiding repetitions of code and information.
- Break down your code and logic into reusable units. Call them where you need them.
- Use helper functions or methods.
- Middleware comes in handy.
In a bid to keep code DRY, many developers fall into the lure of abstracting almost every aspect of the codebase. For you the developer who wrote the code, it's dry, for the next developer going through the codebase is hard. I was overly concerned about keeping it DRY, that I became habituated to abstracting my codes. Time for a rethink is when you hear of AHA programming.
Abstracting a software component means separating what it does from how it does it. This could be achieved by creating a new method or function (what it does) and importing or calling it were you need it (how it does it).
Avoid Hasty Abstractions (AHA) programming is a software development principle popularised by Kent.C.Odds. You can read more about AHA programming here. A basic summary of how to ensure you have avoided the lure of hasty abstractions is summed up in the words Prefer duplication over wrong abstraction, Optimise for change.
Basically, instead of hurriedly creating a helper class that would not necessarily apply to all the cases where the abstraction is needed as changes occur to the codebase, duplicate the code inline.
- Write codes with duplicate codes first for all use cases.
- When you have finished writing, you will see the commonalities, then you can abstract.
The bottom line is, feel free to use abstractions not sparingly but when necessary and don't be afraid to duplicate your code.
When it comes to software design you either Keep It Simple, Stupid (KISS) or risk losing your users. A difficult to navigate application is not usable no matter how useful it is and can't be lovable. What can help you get a kiss on design? Two tips to keep in mind.
- Do user research before designing the products and get an insight into how the user would most likely interact with the app.
- Remember YAGNI so you don't complicate the products.
General Responsibility Assignment Software Patterns (Grasp) are guidelines used in assigning responsibility to classes and objects in Object-Oriented designs. Grasp is made up of 9 basic patterns for assigning responsibility. Here is a summary of them
- Information Expert:- Give a class the responsibility only if it has all the necessary information in that class to fulfill that responsibility.
- Creator:- A class B should only be given the responsibility to create another class A if B contains or aggregates A, B closes uses A, B holds the data for initializing A or B records A.
- Controller:- An object is assigned the role of a controller if the object sits as the first layer after the UI components. A controller handles all user requests coming from the UI by delegating the responsibility to other objects or methods. The controller should be thought of as a service layer.
- Low Coupling:- Assign responsibilities to objects so that the dependency of one to another object is low. That is a change in one does not necessarily affect the other greatly. This makes it have a high potential of being reused. One way to achieve this is by using the indirection principle. Another way to achieve low coupling is to use composition instead of inheritance.
- Indirection:- Assign the responsibility of the interaction between two objects to another object, an intermediate object. This helps to ensure that they are not directly coupled. They become low coupled.
- High Cohesion:- Assign responsibilities to a class or object such that the responsibilities are related. If a class has many responsibilities it would have low cohesion because the class would be handling unrelated responsibilities. On the other hand, an object with a single responsibility has the highest cohesion.
- Polymorphism:- When related behaviors (method) vary by type(class), assign the responsibility of the method to the class in which the variation occurs. A way to achieve polymorphism is method overriding.
- Protected Variations:- To avoid changes or variations of elements to affect other elements, take note of what causes the variations in these elements, create an interface for these variations and use polymorphism for various implementation of the interface.
- Pure Fabrication:- When trying to have an information expert reduces high cohesion and causes tight coupling or it becomes difficult to figure out where to place responsibilities, assign a highly cohesive set of responsibilities to a class that does not represent the domain problem. For example, storing data does not represent a domain problem. It is a service.
How does one build a software like Hollywood? By implementing the Hollywood principle, "Don't call us, we call you". This is a popular phrase used in Hollywood auditions to infer rejections of candidates seen as not fitting. Following that design in software development, the higher-level component dictates how low-level components should be implemented so that no dependency is between them, but not the other way round. Higher-level components call lower-level components not through direct dependence but via abstractions. This is similar to the Dependency inversion principle in SOLID although the Hollywood principle specifies the exact flow.
SOLID principles are a major set of software design principles. They include
- Single Responsibility Principle: - A class, method or function should have only one responsibility i.e it should carry out only one function. A function that checks if a user exists before logging the user in does not follow SRP. That is because it carries out two functions, checks if the user already exists and logging the user. These two actions should be separated into different functions, one for checking if the user exists and the other functions for logging the user in. That way it abides by SRP.
- Open-Closed Principle:- A class or method should be open to extension but closed to modification. This is achievable through inheritance or composition where the base class is extended to any subclass including its methods without modifying the base class. Methods can also be overridden in subclasses ensuring polymorphism. In functional programming, this is achievable using higher-order functions.
- Liskov Substitution Principle:- An object in a program should be substitutable with instances of the object subtype without altering the correctness of the program.
- Interface Segregation Principle:- Clients should not be forced to depend on methods it does not use. Do not add functionality to an existing interface by adding new methods but rather create many client-specific interfaces that contain only what the client needs for its specific use case.
- Dependency Inversion Principle:- Abstractions should not depend on details. Details should depend on the abstractions. High-level modules should not depend on low-level modules. They should both depend on the abstraction.
Lean development is an agile framework where the team creates the minimum viable product, releases to the public, learns from its users and iterate back on feedback. Seven principles that could help you keep your software development cycle are:
- Eliminate waste - During software development, eliminate waste by avoiding extra features, partially done work, switching tasks between teams and unnecessary inventory.
- Build quality in - You can do this by practicing test-driven development and continuous integration, refactoring often, keeping the code base simple and avoid building defects in the software for the first time.
- Create knowledge - Expect your design to evolve from the initial design plan. Therefore create short release cycles, deliver MVP and get knowledge from the users through feedback and evaluation. Then iterate. Learn as you build throughout the process.
- Deliver fast - Create release cycles of equal length so that developers know the timeline for release and do not have to guess. These release cycles should be short so that the software would be delivered fast so that customers would not have time to change their minds about using the products.
- Defer commitment - Delay uncertain decisions in the development process as late as possible when facts have been gathered to aid the decision-making process. It doesn't mean you should not plan, it means that you should plan different options as possible but only decide on one as late as possible when feedbacks and evaluation suggest that the option is the best.
- Respect people - Don't view the people working on the development project as resources but rather as people. They need motivation. They need a higher purpose for doing the work. They also need to be acknowledged for the work they do. Help the members of the team grow their technical skills through training. Empower the team with the right tools to do the work.
- Optimize the whole - While different teams could work on various parts of the software development and in a series of release cycles, the overall aim is to optimize the whole product, the big picture. That means the interactions within various parts of the app should be optimized and bug-free. That ensures the whole value stream is optimized.
You may have followed some of these principles in building that great app, don't forget to Keep It Documented (KID). A good document is essential for maintenance purposes, for transfer of knowledge from one developer or team to another and for development purposes. Good documentation should include installation details, troubleshooting, how to deploy the app in various environments, databases, and files. It should also enable the user to easily navigate the app.
PS: These principles are in no way new. This serves as my software design principle cheatsheet but I am optimistic others will benefit from it in a bid to becoming a better developer.
Cheers to being a better developer in 2020 and the years to come!