Tips For Better Software Engineering

·5 minutes of reading

Photo AltumCode

1. Think conceptually first then resolve details second

Your design choices will guide you throughout the development process. They are your map while you navigate the solution space on your journey to take your ideas from the realm of the abstract to the realm of the concrete (aka. working software).

Always choose the best architecture that captures all of your functional and non-functional requirements. A good architecture must also offer some flexibility for additional changes in the future in case of new requirements or changes of the existing ones. This is essentially one of the S(O)LID principles: software should be open for changes but closed for modifications.

A common criteria for choosing an architecture is its dominant operability dimension: messaging type (synchronous or asynchronous), tier-based separation of concerns (ex. a 3-tier made of a client, server and a database), pipeline-driven (ex. data processing happens within pipeline stages), fault-tolerant and resiliency (ex. the design is focused on the fast recoverability of the system because failure almost always happen). Obviously some of these architectures can be mixed together to produce a unique one that captures all of the possible states of the system even when the latter is pushed to its limits.

Finally a good architecture should have a conceptual model that looks like a pyramid. The top-level components functionalities should be a direct translation of the business requirements and the lowest-level components contain atomic assumptions and solutions to the core issues that the system is addressing.

2. Learn about design patterns

Design patterns are a necessary tool in a developer's arsenal. Not only do they make code cleaner, easier to maintain and to reason about (a mental shortcut), but also they help us produce a working software that work as intended. For new developers, it may be difficult to remember to use design patterns when coding but with enough practise, these patterns become second nature as you begin to use them almost without thinking. An exercise that I like doing sometimes is to identify design patterns inside software libraries.

There are 3 types of patterns:

  • Structural: inheritance, composition, decorator, proxy, flyweight (shared object state), façade (API), adapter
  • Behavioural: iterator, chain of responsibility (ex. Spring security chain), command delegation (ex. HTTP server), inheritance (ex. through polymorphism), observer (ex. an event listener), memento (ex. serializer/deserializer), mediator (ex. inversion of control)
  • Creational: singleton, factory method, abstract factory, builder, prototype

3. Automate the development process

Setup your continuous integration and deployment pipelines in such a way that errors, security vulnerabilities and bugs are automatically detected. There are many tools and approaches to help you do so: artifact versioning (in case you failures, you have to quickly revert back to the last working version of the system), Sonar (for code quality and test coverage), Fortify (for vulnerabilities checks inside third-party libraries), unit and end-to-end tests.

When you are often adding pipelines, it may be useful to create templates for your CI/CD pipelines by externalizing commons tasks into a shareable artifact. Write your pipelines and infrastructure as code, store sensitive data as secrets and make sure to have a policy for periodically rotating these secrets (never store secrets in plain text).

Automate deployment and make your CD pipeline triggerable with a single click from a feature or main branch. Of course main-branch deployment should have strict checks in place like: 4-eyes approval, blue-green deployment (aka canary builds) and monitoring enabled for automated failure detection.

4. Do coding challenges

There are many online coding platforms, I personally use hackerrank and leetcode. These platforms offer coding exercises covering topics like: backtracking, dynamic programming, graph, sorting, etc. Take your time to first solve a challenge with a brute-force solution then think of how to use the problem constraints to your advantage and simplify your algorithm in order to improve the time and space complexities. Once done, rewrite your solution from scratch and redo that process few times maybe in a different language until you fully internalize the solution. Finally extract the concepts and ideas that helped you solve the challenge and explain them out loud to yourself or to someone else.

Thanks for reading. Let me know about your favourite engineering tips. You can reach out to me directly using the contact form.

Profile of Seifeddine Dridi, backend Java/Kotlin engineer

My name is Seifeddine and I am backend-engineer with  12 years of experience building scalable and fault-tolerant web services in Java and Kotlin.

If you are looking for a passionate engineer, I'd be happy to hear from you.