Skip to main content

Coding Best Practices

new project graveyard Software Engineering Scams

Table of Contents

  • Variable Naming
  • Maintainability
  • Refactor VS Rewrite
    • [Lazy initialization]
  • Separation of Concerns
  • Functional Programming
  • Scope: Coding best practices primarily revolve around the implementation level, focusing on how individual lines of code are written and structured within a program.
  • Level of Granularity: Best practices tend to address specific coding techniques, patterns, and conventions that improve code readability, maintainability, and efficiency
  • following naming conventions
  • writing modular and testable code
  • using appropriate data structures and algorithms
  • avoiding code duplication
  • applying error handling mechanisms
  • and adhering to coding style guidelines.

Innersource

Innersource is the practice of applying open source best practices within an organization to encourage collaboration, transparency, and code reuse among teams. It involves treating internal codebases like open source projects, allowing developers to contribute, review, and share code across departments and projects.

Key Aspects of Innersource

  • Encourages code sharing and reuse
  • Facilitates cross-team collaboration
  • Improves code quality and consistency
  • Enhances knowledge sharing and learning opportunities
  • Fosters a culture of transparency and accountability

Coding Principles

Commenting

comments are a neccesary evil, "the proper use of comments is to compensate for our failure to express ourself in code"

comments unneccesary

Procedural Code vs OO Code

Data and object anti-symmetry is a concept introduced in the book Clean Code by Robert C. Martin. It refers to the fundamental difference between how procedural and object-oriented (OO) code handle the relationship between data and functionality.

Procedural Code:

  • Focuses on Data Structures: In procedural code, data structures like arrays, lists, or structs hold the raw data. Separate functions operate on this data, manipulating and transforming it.
  • Adding Functions is Easy: Since the data structures are exposed, adding new functions becomes straightforward. You can write new functions to work with the existing data structures without modifying them. This promotes code reuse for the data structures.
  • Adding New Data Structures is Hard: However, if you need to introduce a new data structure with its own set of functionalities, it can be challenging. All the existing functions that operate on data might need to be modified to understand the new data format. This can lead to tight coupling and difficulty in maintaining the codebase.

Object-Oriented Code:

  • Focuses on Objects: In OO code, data is encapsulated within objects (classes and instances). Objects have both data (attributes) and functionalities (methods) that operate on that data.
  • Adding Classes is Easy: Because objects are self-contained units, adding new classes with their own data and behavior becomes easy. You simply define a new class with its attributes and methods. This promotes code modularity and reusability of functionalities.
  • Adding New Functions to Existing Classes can be Hard: On the other hand, adding new methods to existing classes can be more complex. You might need to modify existing classes to accommodate the new functionality, potentially breaking existing code that relies on those classes.

Here's an analogy to illustrate the concept:

  • Think of a Toolbox (Procedural): Imagine a toolbox with various tools (functions) and separate compartments for different materials (data structures). Adding a new tool is easy, but adding a new compartment with unique handling might require modifying all the existing tools.
  • Think of Building Blocks (OO): Now imagine building blocks like Legos. Each block (class) has its own properties (data) and ways to connect (methods). Adding new blocks is easy, but modifying existing ones to connect in new ways can be trickier.

In summary:

  • Procedural code prioritizes ease of adding new functions but makes adding new data structures difficult.
  • OO code prioritizes ease of adding new classes but makes modifying existing classes to add new functions more challenging.

This concept helps developers choose the right approach based on the anticipated changes in their codebase. If you expect frequent additions of functionalities, procedural code might be a better fit. If you foresee the need for new data types with specialized behaviors, OO might be the preferred approach.

https://javascript.plainenglish.io/20-hilarious-memes-for-those-who-have-worked-in-the-agile-framework-e6c17b4d8260

Writing Readable, Good Code

In a professional environment, the principal audience for any given line of code is not the computer but the developer who has to read that code at some point in the future for further development. This is the golden rule of programming: engineers should be writing code with the same level of readability that they expect of anyone else's code.

Good Code vs Bad Code

  • WTFs/Minute
  • one broken window starts the process toward decay

Picking Tech Stacks based on popularity

language popularity 11 / 2023

Boring Technology

Shiny Object Syndrome (SOS): is the tendency to be easily distracted by new, exciting technologies, leading to a lack of focus on current goals and unfinished projects. It is a common problem for people exposed to new business strategies and technologies. This can result in scattered focus and a pattern of abandoning projects once the initial novelty wears off. In software engineering often leads teams to adopt cutting-edge tech stacks that are overkill for the problem at hand. This misalignment can introduce unnecessary complexity, higher costs, and long-term scalability issues.

https://boringtechnology.club/

Boring Technology is a phrase coined by Dan McKinley and outlined at ctohb.com/boring. The key idea is that your team's job is to deliver functionality to support the business, and most of the time that doesn't depend on using fancy new tools. In fact, using something that's not boring often has many hidden costs, and only if your team is fully cognizant of those costs and believes the benefits are larger, should the new tool be adopted. Some hidden costs to consider:

  • Incomplete, inaccurate, or immature documentation
  • Not fully developed ecosystems around the tool/technology, including SDKs and integrations with other tools
  • A higher likelihood of encountering defects or missing functionality/features
  • Additional training cost for members of your team to adopt the new tool
  • Burden in keeping that tool or package up to date, patch ing security holes, etc.


https://sourcegraph.com/search

most tech stacks are broken down into layers with six categories:

  • Your application
  • Framework
  • Programming Language
  • Database(s)
  • Server Operating System
  • Infrastructure

While there are many clean coding principles advocated by different developers and organizations, some principles have gained broad approval and are widely recognized as best practices. Here are a few popular clean coding principles:

  1. SOLID Principles: SOLID is an acronym for five principles that guide software design and architecture. These principles include Single Responsibility Principle (SRP), Open-Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP), and Dependency Inversion Principle (DIP). Following these principles promotes modular, reusable, and maintainable code.

  2. KISS Principle: KISS stands for "Keep It Simple, Stupid." It advises developers to favor simplicity over complexity in code design. Simple code is easier to understand, debug, and maintain.

  3. DRY Principle: DRY stands for "Don't Repeat Yourself." It emphasizes avoiding code duplication by extracting common functionality into reusable components, functions, or modules. This reduces maintenance effort and the likelihood of introducing bugs when making changes.

  4. YAGNI Principle: YAGNI stands for "You Aren't Gonna Need It." It suggests avoiding the implementation of functionality that is not currently required. This principle prevents over-engineering and unnecessary complexity in the codebase.

  5. Code Readability: Writing code that is easy to read and understand is crucial. Use descriptive names for variables, functions, and classes. Keep functions short and focused. Properly indent and format the code. Well-formatted and readable code improves maintainability and reduces bugs.

  6. Avoid Magic Numbers and Strings: Avoid using hard-coded numbers or strings directly in the code. Instead, define them as constants or variables with meaningful names. This enhances code readability and makes it easier to modify values when needed.

  7. Separation of Concerns: Separate different concerns and responsibilities into distinct modules or components. This promotes modularity, reusability, and makes the code easier to understand and maintain.

  8. Testability: Write code with testability in mind. Design code that is easy to test by following the principles of loose coupling, dependency injection, and separation of concerns. Writing unit tests helps ensure code correctness, catch regressions, and facilitates future modifications.

  9. Error Handling: Implement proper error handling throughout the codebase. Use appropriate mechanisms like try-catch blocks or error boundaries to handle exceptions and errors gracefully. Provide meaningful error messages and handle failures in a way that doesn't compromise the application's stability.

  10. Continuous Refactoring: Regularly review and refactor the code to improve its design, readability, and maintainability. Refactoring helps eliminate code smells, reduces technical debt, and keeps the codebase clean and efficient.

These principles are not exhaustive, but they provide a solid foundation for writing clean, maintainable, and high-quality code. Following these principles can lead to improved productivity, reduced bugs, and enhanced collaboration in software development projects.

As a successful tech entrepreneur coach, I understand the enthusiasm and curiosity of entry-level software engineers when it comes to cutting-edge technologies. However, it's important to guide them towards a more pragmatic approach that emphasizes the value of focusing on OKRs (Objectives and Key Results) and KPIs (Key Performance Indicators) rather than chasing after every new technology trend. Let me make the argument for YAGNI (You Aren't Gonna Need It) and Boring Technology:

1. YAGNI - You Aren't Gonna Need It

meme

https://www.plutora.com/blog/you-aint-gonna-need-it-yagni

YAGNI encourages developers to avoid implementing functionality that is not currently required. By adhering to this principle, your team can avoid unnecessary complexity, development time, and potential bugs caused by over-engineering. Instead, encourage your engineers to focus on delivering the core features that align with the business objectives and customer needs. YAGNI helps maintain a lean development process, allowing your team to iterate quickly and respond to market demands effectively.

2. The concept of Boring Technology, coined by Dan McKinley, 3-30-2015

Developer Hype Cycle

Boring Technology emphasizes the use of proven, stable, and reliable technologies rather than constantly chasing after the latest trends. Boring Technology reduces the risks associated with adopting bleeding-edge technologies, such as compatibility issues, lack of community support, and unforeseen complexities. By utilizing mature and widely adopted technologies, your team can benefit from increased stability, reduced maintenance overhead, and improved developer productivity.

  • embrace boring tech stack. because you have a limited capacity for complexity in your brain, "cognitive overhead". for each moving part you're using up this budget
  • the long-term costs of keeping a system working reliably vastly exceeds any inconveniences you encounter while building it
  • if you think you can't accomplish your goals with what you've got now, you are probably not thinking creatively enough
  • boring should not be conflated with bad
  • polyglot programming is a lie told to promise developers freedom to choose their own tools and this will enable them to be more effective. this is a naive definition of the problems at best, and convincing lies at worst.

3. Focus on OKRs and KPIs

OKRs and KPIs provide clear direction and measurable goals for your team. Rather than being distracted by the allure of new technologies, encourage your engineers to align their efforts with the company's objectives and key results. By focusing on OKRs and KPIs, your team can prioritize the features and improvements that have a direct impact on business success. This approach ensures that the engineering efforts are aligned with the overall business strategy and customer needs, leading to meaningful outcomes and measurable results.

5. Learning and Growth

While it's important for software engineers to stay updated with industry trends, it's equally crucial to cultivate a learning mindset focused on the right technologies at the right time. Encourage your engineers to pursue learning opportunities that align with the company's goals and contribute directly to the required skill set. This approach ensures a balance between personal growth and the needs of the business, fostering a culture of continuous improvement and expertise in relevant areas.

By guiding your entry-level software engineers to prioritize OKRs and KPIs over chasing after cutting-edge technologies, you will help them understand the significance of delivering value, maintaining stability, and aligning their efforts with the overall business strategy. This approach promotes a pragmatic and results-driven mindset, leading to more successful and impactful outcomes for your startup.

Functional programming

a programming paradigm that emphasizes the use of pure functions and immutable data. It is closely related to clean coding principles as it promotes code that is easier to reason about, test, and maintain. Here's how functional programming aligns with clean coding principles:

functional programming

Immutability

Functional programming encourages the use of immutable data, where once a value is assigned, it cannot be changed. This promotes code that is free from unexpected side effects and makes it easier to reason about the behavior of functions. Immutability reduces the risks of bugs caused by mutable state and allows for more predictable and reliable code.

Pure Functions

Functional programming relies heavily on pure functions, which are functions that produce the same output given the same input, without any side effects. Pure functions are independent of external state and do not modify data outside of their scope. They promote code that is easier to understand, test, and reason about since their behavior is solely determined by their inputs and not affected by external factors.

Separation of Concerns

Spaghetti Code to Ravioli Code

https://martinfowler.com/articles/class-too-large.html

Functional programming encourages the separation of concerns by breaking down complex problems into smaller, composable functions. Each function focuses on a specific task and can be combined or reused to solve larger problems. This promotes code that is modular, reusable, and easier to understand and maintain.

Higher-Order Functions

Functional programming leverages higher-order functions, which are functions that take other functions as arguments or return functions as results. Higher-order functions enable code that is more declarative and expressive, as they allow for the composition and combination of behavior. They promote code that is concise and reduces duplication by encapsulating common patterns or operations.

Avoidance of Mutable State

Functional programming discourages the use of mutable state and encourages the use of immutable data structures and transformations. By avoiding mutable state, the code becomes less prone to bugs caused by shared or unexpected changes to data. It also simplifies concurrency and parallelism since there are no shared mutable states to be synchronized.

Data Transformation

Functional programming emphasizes the use of transformations on data rather than mutable updates. Instead of modifying existing data, functional programs often create new data structures based on the original ones. This approach promotes code that is less error-prone and easier to reason about since the original data remains unchanged.

By following the principles of functional programming, developers can create code that is cleaner, more modular, and easier to understand and maintain. The emphasis on immutability, pure functions, separation of concerns, and avoidance of mutable state aligns with the clean coding principles of simplicity, readability, modularity, and minimizing side effects. Functional programming encourages practices that lead to code that is less error-prone, more testable, and ultimately of higher quality.

The logic for domain models in Angular apps is often split between the client and the server. The server contains the persistent store, typically a database, and contains the logic for managing it. In the case of a SQL database, for example, the required logic would include opening connections to the database server, executing SQL queries, and processing the results so they can be sent to the client.

Web application Architecture Img

You don’t want the client-side code accessing the data store directly—doing so would create a tight coupling between the client and the data store that would complicate unit testing and make it difficult to change the data store without also making changes to the client code.

Img

By using the server to mediate access to the data store, you prevent tight coupling. The logic on the client is responsible for getting the data to and from the server and is unaware of the details of how that data is stored or accessed behind the scenes.

Domain Model Img

Application Layers

Applications based on JavaScript frameworks, such as React and Angular, typically handle their own internal routing. However, the routing happens within the application and not at the web server. Since the routes are not visible to the web server they will not be served by the web server.

REFACTOR vs REWRITE

https://www.techtarget.com/searchapparchitecture/tip/Refactor-vs-rewrite-Deciding-what-to-do-with-problem-software

Refactoring

https://www.industriallogic.com/xp/refactoring/catalog.html

refactoring is a development technique, not a project, Refactoring happens on an ongoing and relatively minor basis. If you undertake something that constitutes a project, you’re changing the software. You’re altering the way it interacts with the database, swapping out a dependency, updating your code to a new version of the framework, etc.

REWRITE

Perhaps the software runs on dated hardware or perhaps it has outsize dependencies on dying software. It could be that new feature requests are radically incompatible with the existing architecture or that the way it interacts with other systems has evolved. It might even be that the software is simply a mess, but your team inherited it rather than being the ones responsible for it.

Reactive Systems: a way of architecting systems that are

  • responsive,

  • resilient,

  • elastic, and

  • message driven

  • By working directly with the interface, you are not acknowledging, and coupling your code to the underlying implementation.

  • In the event that you already have, say, a public property Name in your code and you want to implement an interface that also has a Name property, doing it explicitly will keep the two separate. Even if they were doing the same thing I'd still delegate the explicit call to the Name property. You never know, you may want to change how Name works for the normal class and how Name, the interface property works later on.

  • If you implement an interface implicitly then your class now exposes new behaviours that might only be relevant to a client of the interface and it means you aren't keeping your classes succinct enough (opinion).

Maintainability

  • methods exceeding 30 lines of code
  • McCabe Cyclomatic Complexity exceeding 10
  • methods having more than 4 parameters
  • classes exceeding 20 incoming dependencies
  • more than 6 lines of duplicate code

Table of Contents ⬆️

Extract Method Refactoring - 1 method says what it does, extract some submethods saying how its done

Naming Convention

  • informative
    • explaination of intent
  • solution domain names
  • one level of abstraction per function
    • step down discrete statements
  • one word per concept