# A Practical Guide to Common Best Programming Best Practice :::info :warning: This article only represent me, an indie software engineer who likes to explore new things. Read it with a grain of salt! ::: > Shut up, just ship faster! One might think, this topic is boring and kind of useless. Well, That's also me until I started developing projects that's incrementally getting bigger and bigger. Trust me, If you are developing and maintaining medium to big projects with incremental update (features, integrations and bug fixing) you are gonna **NEED THIS**. If you are only doing small projects and still think this article is useless, surely you are free to go. This article will be waiting for you any time in the future :crossed_fingers: <br> ## ❓ What is Programming Best Practice? ![confusion](https://hackmd.io/_uploads/HyGXP28C6.png) <p style="text-align:center;font-style:italic"> image taken from <a href="https://tech.zensurance.com/posts/spaghetti-code" target="_blank">Zensurance</a> </p> in software engineering or software development -- in general -- as the software features get "big", usually -- and most likely always -- It isn't managed by a single person, rather a certain group of people (programmers). Those very "programmers" undisputably have their own coding styles and might differs from each other. Now imagine if for a single medium projects there are 5 people and each codes the project with different approach and style, how would the integrated feature be? Yes, a **mess**. You will find it hard to understand the codebase, let alone refactor and incremental update. This is the very reason why such thing as "Programming Best Practice" exists. It's to ensure that softwares are built following certain constraints, rules, and guides so that they are generally easier to be understood and maintained by programmers who are and who will be in charge of those projects onward. Let's discuss some common principles :sunglasses: <br> <br> ## 👊SOLID Principle ![blg_inline_solid_principles](https://hackmd.io/_uploads/B1QADhUCT.png) <p style="text-align:center;font-style:italic"> SOLID Principles taken from <a href="https://www.thoughtworks.com/insights/blog/agile-engineering-practices/solid-principles-how-to-create-a-code-that-is-easy-to-extend-and-maintain-part-1" target="_blank">Thoughtworks</a> </p> SOLID principles, iniated by Robert C. Martin, is a principle that offers some guideline for writing clean and maintainable object-oriented code. Each principle addresses specific aspects of software design which collectively contributing to code that is modular, flexible, and easy to understand or maintain. The name ***SOLID*** actually comes from the abbreviation of 5 Principles that it offers, Let's discuss it one by one. >**NOTE** : SOLID Principles was made from and for purely object-oriented language, such as Java. ### Single Responsibility Principle <br> <p style="font-size:18px; font-weight:bold; font-style:italic;text-align:center;"> "Every Class should only have a single responsibility" </p> This Principle is a very straightforward one. It ensures that a specific class is made focusing on functional aspects that is naturally and logically in the same domain to achieve cohesive and easily-understandable code. Let's use some example. Suppose for your convenient store program, you want implement feature to count subtotal purchasement for every customer and you want also feature to do journal on ledger and cashflow statement. rather than creating a single class to handle these, you can start thinking trying to divide those functionality into several "domain". Then, you create class from each of those grouped functionality. using the same example, we can try to create 2 class, Cashier and Accountant which have functionality related to their position. ```java= Public Class Cashier { . . . public int countTotal(ArrayList<Item> items){ // Implement the logic here } } Public Class Accountant { . . . public void doJournal(){} public void auditLedger(){} ``` Therefore, each class is responsible only for a single (domain) of functionality, that is , highly cohesive. <br> ### Open/Closed Principle (OCP) <br> <p style="font-size:18px; font-weight:bold; font-style:italic;text-align:center;"> "Every Class should be open for extension, but closed for modification" </p> This principle suggest us that for certain classes, you should provide a way that for whenever we want to add some functionality to certain class, we could do so by only extending, not modifying the source code of respective class. Let's use 2D geometrical shape as example. ```java= public abstract class Shape { public abstract double area(); } public class Rectangle extends Shape { private double length; private double width; public Rectangle(double length, double width) { this.length = length; this.width = width; } @Override public double area() { return length * width; } } ``` For the example above, we can see the *"open for extension"* takes place at `Rectangle` extending base class `Shape`. All of this is done without modifying the base class `Shape`. If needed you can create a different geometric shape class that is also extending `Shape`, for example, `Circle` with different attributes and implementation of area function etc. <br> ### Liskov Substitution Principle (LSP) <br> <p style="font-size:18px; font-weight:bold; font-style:italic;text-align:center;"> "Objects of a subclass should be able to replace objects of the superclass without affecting the correctness of the program." </p> This principle simply emphasis the substitutability between classes with its superclass or subclass (children) without introducing unexpected behaviour. In short term, it offers polymorphic behaviour. I know it sounds weird and confusing, let's just use Java as example. ```java= // Bird.java public class Bird { public void fly() { System.out.println("Flying..."); } } // Duck.java public class Duck extends Bird { @Override public void fly() { System.out.println("Duck flying..."); } } // Ostrich.java public class Ostrich extends Bird { @Override public void fly() { // Ostriches cannot fly, so we override the fly method to do nothing System.out.println("Ostrich cannot fly."); } } // Main.java public class Main { public static void main(String[] args) { Bird duck = new Duck(); Bird ostrich = new Ostrich(); duck.fly(); // Output: Duck flying... ostrich.fly(); // Output: Ostrich cannot fly. } } ``` In the example above all of the superclass and its children class have `fly()` method. On the `main()` function both Duck and Ostrich class are `casted` onto `Bird` class and each object's `fly()` method still works fine as expected. This is the substitability that LSP offers. <br> ### Interface Segregation Principle (ISP) <br> <p style="font-size:18px; font-weight:bold; font-style:italic;text-align:center;"> "Interfaces need to be segregated based on the functionality they provide, rather than making a big and monolithic interface" </p> This principle is very straightforward. It suggest us to segregate (separate ) interfaces onto more simple fraction. This is very useful when we have different classes which has some overlapping function with different implementation. let's use example ```java= public interface Worker { void work(); } public interface Eater { void eat(); } public class Robot implements Worker { @Override public void work() { System.out.println("Robot working..."); } } // Human.java public class Human implements Worker, Eater { @Override public void work() { System.out.println("Human working..."); } @Override public void eat() { System.out.println("Human eating..."); } } ``` On the example above we see that all both Robot and Human class does `work()` with different implementation, and also that Human class has other functionality that Robot doesn't have, in this case `eat()`. This the segregation ISP offers. so that we have more ganular interfaces. <br> ### Dependency Inversion Principle (DIP) <br> <p style="font-size:18px; font-weight:bold; font-style:italic;text-align:center;"> "High-level modules should not depend on low-level modules; both should depend on loose-coupling, abstractions" </p> This is also confusing. to make it simple, you can think of it similar to LSP, but instead of object or class inheritance, we are discussing about interface here. let's use example ```java= // Logger.java (Abstraction) public interface Logger { void log(String message); } // ConsoleLogger.java (Concrete implementation) public class ConsoleLogger implements Logger { @Override public void log(String message) { System.out.println("Logging to console: " + message); } } // FileLogger.java (Concrete implementation) public class FileLogger implements Logger { @Override public void log(String message) { System.out.println("Logging to file: " + message); } } // BusinessService.java (High-level module) public class BusinessService { private Logger logger; // Constructor injection of Logger dependency public BusinessService(Logger logger) { this.logger = logger; } } // Main.java public class Main { public static void main(String[] args) { // Creating instances of Logger implementations Logger consoleLogger = new ConsoleLogger(); Logger fileLogger = new FileLogger(); ``` On the example above, both `FileLogger` and `ConsoleLogger` implements the same interface, and inside the `BusinessService` the logger are casted onto interface `Logger` (higher abstraction), rather than the `Class` (specific implementation). This is what DIP suggests through decoupling to ensure the easeness of maintenance and ingeration onward. <br> <br> ## Don't Repeat Yourself (DRY) The next principle other than SOLID is DRY. DRY basically addresses 2 important (key) things, that is *code duplication* and *code reusability*. The fundamental idea behind DRY is to avoid duplicating code by abstracting common functionalities into reusable components. This helps in minimizing redundancy and improving maintainability of your software. There's no specific rule or constraint for this principle other than those 2, so giving random example might not be suitable for this one. But if you are curious, Modular Component approach in react is a great example that implicitly introduce us to use such approach (modularity). <br> ## KISS (Keep It Simple, Stupid) In contrast to the 2 design principle before, KISS is a design principle that advocates for simplicity in design and implementation. It suggests that solutions should be kept as simple as possible, avoiding unnecessary complexity. Same with DRY concept, there's no some fancy and specific principle for this design concept, therefore there's no concrete example for this, but There are some strategies to achieve what's KISS offers, those are 1. **Modularity**: Break down complex systems into smaller, more manageable modules. Each module should have a clear purpose and well-defined boundaries. 2. **Avoid Over-Engineering**: Focus on solving the problem at hand without introducing unnecessary complexity, rather than overcomplicating the solution and system 3. **Refactor Regularly**: Continuously review and refactor code to eliminate redundancy and complexity 4. **Keep Designs Minimal**: Aim for minimalistic designs that achieve the desired functionality without unnecessary complexity. <br> ## :books:Which Design Principle to Choose? There's no specific rule to choose which design concept you have to use. It's all up to you (and your team) to use the one that adequates your needs and purposes. And in the end it also depends to programming language and framework you use. Each framework usually adopt certain design principle by considering the programming language it's running on. As of now, there are already many frameworks -- at least for fullstack webapp -- that combines different design principle on certain part. There's no saying what kind of design concept / principle will we invent in the future. One thing we need, is the ability to adapt onto different design principle and ecosystem of frameworks. <br> ## 🧭Ensuring Software Quality Now that you have learnt some best practice or principles in programming, you can try to implement it in real projects. But, the thing is usually in actual project development people won't bother to keep reviewing them codes qualities over and over only to make sure each developer work follows certain principle or design as it really time consuming. To resolve this issue, there's a thing called "Static-code analysis" that can ease the effort of ensuring programming best practice. ### 📊Static Code Analysis Static Code Analysis is a mean to ensure code quality by analyzing code statically, that is -- including, but not limited to -- scrutinizing source code without executing it, uncovering bugs, security vulnerabilities, and adherence to coding standards. Usually, in common Software Quality Assurance techniques, there are 6 topmost aspects, they are audit, reviews, code inspection, design inspection, simulations, and stress test. For this article, Static code analysis we are going to discuss will only address code inspection, audit, and reviews. ### ⚙ Static code Analysis with Sonarqube ![Sonarqube](https://hackmd.io/_uploads/Bk_HUh8kC.png) Sonarqube is a service as well as platform that provides arguably comprehensive static code analysis of your project. It can do tasks such as analyzing code coverage, code duplication, maintainability, even security hotspot of your application for **FREE!**. And what's even cooler is that you can integrate into any common CI/CD framework; that is CI/CD pipeline agnostic. Therefore removing excess effort to put onto usage. for example, ```yml SonarQube: stage: analyze image: gradle:latest services: - "postgres:latest" variables: SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task cache: key: "${CI_JOB_NAME}" paths: - .sonar/cache dependencies: - Test before_script: - echo `pwd` - chmod +x ./gradlew script: - ./gradlew sonarqube --stacktrace --info -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN_TOKEN allow_failure: true only: - main - post-sprint-revision - ci/sonar ``` ![image](https://hackmd.io/_uploads/BJAQF3U10.png) <p style="text-align:center;font-style:italic"> Example of Sonarqube Project Dashboard </p> For this article, i won't be discussing technical implementation of sonarqube as it will be out of scope, so it is ***left to reader as exercise*** :hand_with_index_and_middle_fingers_crossed: > Keep in mind that sonarqube doesn't support all available programming language. <br> ### 🗾Code Coverage ![image](https://hackmd.io/_uploads/Sk0RtnU10.png) <p style="text-align:center;font-style:italic"> Example of Code Coverage Report </p> Code coverage refers to the measure of how much of your source code is exercised by your tests; A metric used to evaluate the effectiveness of test suite by determining which parts of your source code are executed during testing. Sonarqube's code coverage provides detailed insights by showing the percentage of lines, branches, and conditions covered by tests. This information helps you to identify areas of code that lack sufficient test coverage, therefore you can create more tests to assure your project robustness. ### 🔐Security Hotspot ![image](https://hackmd.io/_uploads/B1g2h2LyA.png) <p style="text-align:center;font-style:italic"> Example of Security Hotspot Report </p> Sonarqube can also provides you with insight on security with detailed information based on common known web security "testing guide framework", OWASP WSTG and Portswigger. Sonarqube will scan and match some code that resembles common known vulnerability, and classify those findings by criticality or priority with elaboration regarding the risk and its mitigation. This is really crucial if you want to release your project to production as it can save you a lot of works, rather than manually handling incident response of attack in live production. ### ➿ Code Duplication ![image](https://hackmd.io/_uploads/Hy2JJpIkR.png) <p style="text-align:center;font-style:italic"> Example of Code Duplication Report </p> Sonarqube can also analyze source code to identify duplicate blocks, methods, or classes. This feature helps you uncover redundant code, which can lead to maintenance challenges and technical debt, such as inconsistency in updates and increased code complexity. Sonarqube will highlight line of code that is duplicated and even give you some refactoring insight, thus allowing you to refactor the source code which promotes better maintainability, readability, and overall code quality. ## Conclusion Best practice of programming principle is a must-have knowledge for every software engineer. It helps us to create high quality software that is robust and easy to maintain, therefore promoting easeness of development experience across developers team throughout the time. To achieve that we can leverage automated tools, service, or platform to reduce effort and complexity so that we can focus on developing the software.