Clean Code & Architecture, Style Guide#
Clean Code#
Clean code is a concept in software development that emphasizes writing code that is simple, readable, and maintainable. It’s essentially about writing code that not only works well but is also easy for other developers (including your future self) to understand and modify. Here are some fundamental principles of clean code:
Readability: The code should be easy to read, almost like a well-written prose. This involves using clear variable and function names, proper indentation and formatting, and commenting where necessary.
Simplicity: Code should be as simple as possible. Avoid complex structures and over-engineering. Remember the KISS principle - Keep It Simple, Stupid.
Clarity of Intent: Anyone reading the code should be able to easily understand what the code is meant to do. Good variable and function names help immensely in this.
Consistency: Use the same coding conventions and patterns throughout the codebase. This could involve naming conventions, structure of classes and functions, or even the way you handle errors.
Modularity: The code should be divided into logical modules or functions, each with a clear, single responsibility. This is related to the Single Responsibility Principle (SRP) in SOLID principles of object-oriented design and programming.
Comments where necessary: While it’s often said that “good code is self-documenting,” sometimes comments are needed to explain why something is being done a certain way, especially if it’s not immediately apparent.
Avoidance of code duplication: Duplication can lead to inconsistencies and bugs and should be avoided. This is connected with the DRY (Don’t Repeat Yourself) principle.
Testability: The code should be designed in a way that makes it easy to test. Unittests are an integral part of clean code as they ensure the correctness of the code.
These principles were largely popularized by Robert C. Martin in his book “Clean Code: A Handbook of Agile Software Craftsmanship.” His guidelines have since become widely accepted in the software development industry. Remember, the goal of clean code is to make the code understandable and maintainable, which in turn increases the overall quality of the software and decreases the cost of changes and fixes down the road.
Clean Architecture#
Clean Architecture is a term coined by Robert C. Martin (Uncle Bob), one of the authors of the Agile Manifesto and a well-respected software engineer. In his book “Clean Architecture: A Craftsman’s Guide to Software Structure and Design”, he describes a set of practices and principles that help developers create software architecture that is independent of any specific technology, database, or web server.
The idea behind Clean Architecture is to make the system easy to understand, maintain, and extend over time. The key to achieving this is to make the system’s architecture decoupled and modular. The fundamental rules of Clean Architecture are as follows:
Independence of Frameworks: The architecture does not depend on specific libraries or software frameworks. This allows you to use these tools as helpers without tying your system to them.
Testability: The business rules can be tested without the UI, database, web server, or any external element.
Independence of UI: The UI can change without affecting the rest of the system. The same goes for the database and any other external agency.
Independence of Database: The system doesn’t rely on any specific database. You can swap out Oracle for SQL Server, for example, and your system won’t care.
Independence of any external agency: Your business rules simply don’t know anything at all about the outside world.
The architecture can be visualized as a set of concentric circles, with the most fundamental and high-level modules (entities, use-cases) in the center, and the more detailed and low-level modules (controllers, UI, external interfaces) towards the outer circles. Dependencies point inwards, and the outer circles contain the specifics (like web, database, UI specifics).
In summary, Clean Architecture is about the separation of concerns, which results in a system that is flexible, testable, and easy to understand and maintain. It allows the decoupling of software from the ever-changing technological landscape, allowing it to remain stable while adapting to new technologies over time.
Style Guide#
A style guide in the context of programming languages is a set of conventions or standards for writing code. These conventions often cover file organization, indentation, comments, declarations, statements, white space, naming conventions, programming practices, programming principles, programming rules of thumb, architectural best practices, etc.
The main purpose of a style guide is to achieve consistency in the codebase. When a team of developers is working on a project, it’s important for everyone to write code in a similar style, so that it’s easier for any team member to read and understand anyone else’s code.
Some well-known style guides include:
Style Guide |
Description |
|---|---|
A popular style guide for JavaScript that many JavaScript developers follow. |
|
Google has published style guides for many languages including C++, Java, Python, JavaScript, and others. |
|
A set of standards to help create and maintain clear, concise, and consistent technical content related to MongoDB for both external and internal audiences. |
|
This is the style guide for Python code. It covers topics like indentation, comments, naming conventions, etc. |
|
A community-driven Ruby style guide. |
These style guides are often adopted, wholly or in part, by teams working with the relevant programming languages. Some teams also develop their own style guides that best fit their team’s preferences and needs.
Using a style guide can make the code more readable, maintainable, and robust. Modern IDEs (Integrated Development Environments) and linters can often help enforce style guide rules, highlighting places where your code diverges from the set guide.
File Structure#
When adding headers to your files, it’s beneficial to provide metadata that can help both developers and tools understand and maintain the file. Here’s what you typically might include:
1. File Name#
Purpose: Clearly indicate the file’s name for reference.
Format:
Python:
Filename: example.pySQL:
-- Filename: example.sql
3. Date Created#
Purpose: Provides a historical context, especially useful for tracking changes over time.
Format:
Python:
Date Created: YYYY-MM-DDSQL:
-- Date Created: YYYY-MM-DD
4. Last Modified#
Purpose: Helps to keep track of the most recent changes.
Format:
Python:
Last Modified: YYYY-MM-DDSQL:
-- Last Modified: YYYY-MM-DD
5. Description#
Purpose: A brief summary to explain the purpose or functionality of the file.
Format:
Python:
Description: This script connects to the database and fetches user data.
SQL:
-- Description: This script creates the user table and sets up primary and foreign keys.
6. Version (optional)#
Purpose: Helps to manage different versions of the file.
Format:
Python:
Version: 1.0.1SQL:
-- Version: 1.0.1
7. Dependencies (primarily for Python)#
Purpose: Lists any external modules or packages the script depends on.
Format:
Dependencies: pandas, sqlalchemy
8. Usage (optional but helpful)#
Purpose: Briefly describe how to use the file or any required parameters.
Format:
Python:
# Usage: python example.py --database=mydbSQL:
-- Usage: Execute this script to set up initial database tables.
9. License (if applicable)#
Purpose: Indicate the licensing terms.
Format:
Python:
License: MIT License
SQL:
-- License: MIT License
Remember to keep headers updated, especially the “Last Modified” field, to ensure they reflect the current state and purpose of the file.
Versioning#
The version numbering system, often referred to as “semantic versioning,” is a standardized way to represent the different stages of software development and the types of changes made between releases. It provides clear, structured information about the software’s progress and compatibility.
A typical version number looks like this: MAJOR.MINOR.PATCH. Let’s break down each component:
1. MAJOR#
Purpose: Indicates breaking changes that aren’t backward-compatible.
When to Increment: You’d increase the MAJOR version when making incompatible changes that require the user to change something about their setup. This is commonly associated with significant overhauls of a software package.
For instance, if version
1.x.xof an API provides a certain set of endpoints, and version2.0.0removes or changes those endpoints, then anyone using version1.x.xwould face issues if they tried to upgrade without making changes to their implementation.
2. MINOR#
Purpose: Represents the addition of new features in a backward-compatible manner.
When to Increment: You’d increase the MINOR version when adding new features that don’t break the existing functionality or features of the software. Users can upgrade the software with minor versions without changing their current setup or implementation.
For example, if version
1.2.xof a software library provides certain functionalities and version1.3.0adds a new functionality without disrupting the existing ones, then a user can safely move from1.2.xto1.3.0without expecting their current setup to break.
3. PATCH#
Purpose: Denotes bug fixes and minor improvements that are backward-compatible.
When to Increment: The PATCH version is increased when making backward-compatible bug fixes. These fixes generally resolve issues that don’t affect the software’s overall functionality or features.
Suppose version
1.2.3of an application has a minor bug that occasionally causes it to crash. If the bug is fixed in version1.2.4, it indicates that no new features were added, and no existing functionality was altered. Only the bug was addressed.
To summarize:
MAJOR changes are disruptive and not backward-compatible.
MINOR changes introduce new features but remain backward-compatible.
PATCH changes are for bug fixes and are backward-compatible.
In addition to these, some software may have pre-release versions or build metadata, like 1.0.0-alpha.1 or 1.0.0+20130313144700, but the major-minor-patch structure is the foundation of semantic versioning.