1. mono repo
2. single source of truth
2.1. database
3. event driven design
3.1. cqrs
3.2. saga
3.3. data redundancy
4. ci cd
5. patterns
5.1. Repository
5.2. unit of work
5.3. Saga pattern
5.4. Specification pattern
5.5. Fluent API
5.6. Mediator
5.7. chain of responsibility
5.8. observer
5.9. Template method
5.9.1. Main
6. Code Smells
6.1. Bloaters
6.1.1. Long Method
6.1.2. Primitive Obsession
6.1.3. Data Clumps
6.1.4. Large Class
6.1.5. Long Parameter List
6.2. Object-Orientation Abusers
6.2.1. Alternative Classes with Different Interfaces
6.2.2. Refused Bequest
6.2.3. Switch Statements
6.2.4. Temporary Field
6.3. Change Preventers
6.3.1. Divergent Change
6.3.2. Parallel Inheritance Hierarchies
6.3.3. Shotgun Surgery
6.4. Dispensables
6.4.1. Comments
6.4.2. Data Class
6.4.3. Lazy Class
6.4.4. Duplicate Code
6.4.5. Dead Code
6.4.6. Speculative Generality
6.5. Couplers
6.5.1. Feature Envy
6.5.2. Incomplete Library Class
6.5.3. Middle Man
6.5.4. Inappropriate Intimacy
6.5.5. Message Chains
6.5.5.1. In code you see a series of calls resembling a->b()->c()->d()
6.5.5.1.1. Reasons for the Problem
7. styles
7.1. bad
7.2. good
7.3. not as good
7.4. not as bad
7.5. important
7.6. warning
8. API first approach
8.1. modern
8.2. Traditional "code-first"
8.2.1. development can result in delays, rework, and a disjointed developer experience due to late API integration.
8.3. API-first design
8.3.1. It prioritizes the API's creation before the main application, promoting better cohesion and future integrations.
8.3.2. API-first methods align with agile practices,
8.3.2.1. allowing early stakeholder feedback and design adaptability.
8.3.3. API-first development emphasizes making functionalities available as APIs initially,
8.3.3.1. ensuring stability and predictability.
8.3.4. API-first design involves thorough planning before building the API,
8.3.4.1. focusing on functionality, scalability, and developer experience.
8.3.5. Hiram's Law
8.3.5.1. "With a sufficient number of users of an API, it does not matter what you promise in the contract; all observable behaviors of your system will be depended on by somebody."
8.3.5.2. highlights the challenge of separating interface from implementation
8.3.5.3. the importance of understanding consumer reliance.
8.3.5.4. in simple form
8.3.5.4.1. If many people use a tool (like an API), they'll use it in ways you didn't always expect.
8.3.5.4.2. They'll come to rely on both what you officially tell them (documented features) and things they discover themselves (undocumented behaviors).
8.3.5.4.3. If you change any part of that tool, even the undocumented parts, you might break how they use it.
8.3.5.4.4. This means when making tools for others, you need to be careful about changes and think about all the ways users might have adapted to the tool's behaviors.
9. Objective
9.1. in a nutshell
9.1.1. create a system that can pass the test of time
9.1.1.1. scalability
9.1.1.2. dependency control
9.1.1.3. reliability
9.1.1.4. reusability
9.1.1.5. extensibility
9.1.1.6. interprobabibility
9.1.1.7. standardization
9.1.1.8. being agnostic
9.1.1.9. low barrier to entery, high celling
9.1.1.10. be agile
9.1.1.10.1. refactoring
9.1.1.10.2. iteration
9.1.1.11. business side
9.1.1.11.1. transparency
9.1.1.11.2. network effect
9.1.1.11.3. gamification
9.1.1.11.4. ikea effect
9.1.1.11.5. build for publicity
9.1.2. since failure is inevitable, let's embrace it
9.2. detail
9.2.1. Scalability
9.2.1.1. example
9.2.1.1.1. micoservices
9.2.1.1.2. domain driven design DDD
9.2.1.1.3. Event driven design EDD
9.2.1.1.4. dependency injection
9.2.1.1.5. factory patterns
9.2.1.1.6. command query responsibility query CQRS
9.2.1.1.7. cloud
9.2.1.1.8. Test driven development
9.2.1.1.9. solid principles
9.2.1.1.10. CI CD
9.2.1.1.11. infrastructure as code
9.2.2. Dependency control
9.2.2.1. decoupling
9.2.2.1.1. service decoupling
9.2.2.1.2. data decoupling
9.2.2.2. example
9.2.2.2.1. dependency injection
9.2.2.2.2. Domain driven design
9.2.2.2.3. event driven design
9.2.2.2.4. solid principles
9.2.3. Maintainability
9.2.3.1. single source of truth
9.2.3.2. solid principles
9.2.3.3. test driven development
9.2.3.4. back compatibility matters
9.2.3.5. issues
9.2.3.5.1. if your employee resign would you die?
9.2.3.5.2. if your supplier could not supply you, would you die?
9.2.3.5.3. Can you continue the business forever?
9.2.4. reliability
9.2.4.1. automated test
9.2.4.2. ci cd
9.2.4.3. infrastructure as a code
9.2.4.4. Why?
9.2.4.4.1. how reliable is your system?
9.2.4.4.2. Is it full of bugs?
9.2.4.4.3. can you catch bugs before you ship?
9.2.5. interprobability
9.2.5.1. open portocols
9.2.5.2. plugin systems
9.2.5.3. Why?
9.2.5.3.1. Is your product can talk to other products or services?
9.2.6. reusability
9.2.6.1. example
9.2.6.1.1. Domain driven design
9.2.6.1.2. Solid principles
9.2.6.1.3. microservices
9.2.6.2. Why?
9.2.6.2.1. Can you reuse the code?
9.2.6.2.2. Or should you reinvent the wheel everytime you face an exact same issue?
9.2.6.2.3. Can you leverage past for the future?
9.2.7. standardization
9.2.7.1. follow standards
9.2.7.2. not reinventing the wheel
9.2.7.3. repeatability of the patterns
9.2.7.4. Why?
9.2.7.4.1. Why to reinvent the wheel, if someone else has done it before?
9.2.7.4.2. muscle memory
9.2.8. extensibility
9.2.8.1. example
9.2.8.1.1. plugin systems
9.2.8.2. Open close principle
9.2.8.3. Why?
9.2.8.3.1. can you build a code that could be easily upgraded without impacting existing codes
9.2.8.3.2. ship first develop later
9.2.9. being agnostic
9.2.10. low barrier to entery, high celling
9.2.11. be agile
9.2.11.1. refactoring
9.2.11.2. iteration
10. Solid principles
10.1. Principles
10.1.1. Interface Segregation Principle
10.1.1.1. description
10.1.1.1.1. Client should not be forced to implement an interface that it will never use or interface that is irrelevant to it.
10.1.1.2. good link
10.1.1.3. example
10.1.1.3.1. problem
10.1.1.3.2. Solution
10.1.1.4. example 2
10.1.1.4.1. seperate online payment from offline payment
10.1.1.5. Advantages
10.1.1.5.1. By implementing smaller interfaces we are able to separate responsibilities
10.1.1.5.2. By implementing smaller interfaces we are able to distribute responsibilities among multiple interfaces and thus achieve abstraction.
10.1.1.5.3. Classes can use relevant interfaces and thus implement functions that are required by the classes. So we are able to keep the class clean by keeping out code that is of no use to the class.
10.1.2. Liskov Substitution Principle
10.1.2.1. description
10.1.2.1.1. Any function or code that use pointers or references to base class must be able to use any class that is derived from that base class without any modifications
10.1.2.2. All implementation of a interface should be perfectly implemented and swappable
10.1.2.2.1. This principle suggests that you should write your derived classes in such a way that any child class (derived class) should be perfectly substitutable in place of its parent class (base class) without changing its behaviour.
10.1.2.3. In short
10.1.2.3.1. all drived classes should give the same output for the given input.
10.1.2.3.2. a fact
10.1.2.4. Link much better explanation
10.1.2.4.1. If substituting a superclass object with a subclass object changes the program behavior in unexpected ways, the LSP is violated.
10.1.2.4.2. Why this is an issue?
10.1.2.4.3. How to Identify LSP Violations?
10.1.2.5. example
10.1.2.5.1. example 1
10.1.2.6. Advantages
10.1.2.6.1. Makes subclasses swappable
10.1.3. Open Close Principle
10.1.3.1. description
10.1.3.1.1. A software class or module should be open for extension but closed for modification
10.1.3.2. If we have written a class then it should be flexible enough that
10.1.3.2.1. we should not change it (closed for modification) until there are bugs
10.1.3.2.2. but a new feature can be added (open for extension) by adding new code without modifying its existing code.
10.1.3.3. This principle says that it should be possible to extend functionality in classes without modifying the existing code in the classes
10.1.3.3.1. it should be possible to extend the behaviour of the software without modifying its core existing implementation.
10.1.3.4. implementation
10.1.3.4.1. By open for extension
10.1.3.5. example
10.1.3.5.1. how to do it wrong
10.1.3.5.2. correct approach
10.1.3.6. Advantages
10.1.3.6.1. loose coupling
10.1.3.6.2. Avoid modification of existing code
10.1.4. Single Responsibility Principle
10.1.4.1. description
10.1.4.1.1. Each software module or a class should have one and only one reason to change
10.1.4.1.2. it performs one single task
10.1.4.2. Example
10.1.4.2.1. What not to do
10.1.4.2.2. solution
10.1.4.3. Why not?
10.1.4.3.1. If you add more than one responsibility or task into a single class then we end up with TIGHTLY COUPLED functionalities that should have not been together
10.1.4.3.2. This is difficult to maintain as a change in one functionality might impact other functionality.
10.1.4.3.3. this is against atomic design
10.1.4.4. Advantages
10.1.4.4.1. Reusability
10.1.4.4.2. Improve maintainability by Minimizing data coupling impact
10.1.4.4.3. Improves testability
10.1.4.4.4. Minimize modification
10.1.4.4.5. seperation of concerns
10.1.4.4.6. easier design and implement
10.1.4.4.7. readability
10.1.4.4.8. easier debuging
10.1.4.5. Summary
10.1.4.5.1. By using the single responsibility principle we can reduce dependency between functionalities and hence can better manage our code for implementing new features over the long run.
10.1.5. Dependency Inversion Principle
10.1.5.1. description
10.1.5.1.1. High level classes should not depend on low level classes instead both should depend upon abstraction.
10.1.5.1.2. Abstraction should not depend upon details infact details should depend upon abstraction
10.1.5.1.3. This principle suggests that there should be loose coupling between high-level and low-level classes and to achieve this loose coupling components should depend on abstraction.
10.1.5.1.4. names
10.1.5.2. example
10.1.5.2.1. example 1
10.1.5.3. Advantages
10.1.5.3.1. Make the code more testable
10.1.5.3.2. we can mock interfaces easily
10.1.5.3.3. implementations become swappable
10.1.5.3.4. Classes depend on abstraction and not on concrete types
10.1.5.3.5. High-level and low-level classes are loosely coupled
10.1.5.3.6. As long as you are not changing contracts change in one class will not trigger a change in another class
10.1.5.3.7. Since classes depend on abstraction change in one class will not break another class
10.2. why
10.2.1. maintenance
10.2.1.1. as a result, you need to modify the code
10.2.1.2. example
10.2.1.2.1. add features
10.2.1.2.2. fix bugs
10.2.1.3. The design of the solution should be such that it should be easy to modify or extend existing code
10.2.2. scalability
10.2.3. reusability
10.3. Solution
10.3.1. implement code such that you are able to make a design with considerations for
10.3.1.1. flexibility
10.3.1.2. extendibility
10.3.1.3. readability
10.3.1.4. maintainability
10.4. link
11. This principle simply says that you should introduce abstraction between high-level and low-level classes that allows us to decouple the high-level and low-level classes from each other.
11.1. If classes depend on each other then they are tightly coupled to each other.
11.1.1. When classes are tightly coupled then change in any one class triggers changes in all other dependent classes as wel
11.1.2. Instead, low-level classes should implement contracts using an interface
11.1.3. high-level classes should make use of these contracts to access concrete types
12. setters & getters
12.1. Why?
12.1.1. You want to have a gateway to control the access to properties
12.1.1.1. application examples
12.1.1.1.1. logging
12.1.1.1.2. validation
12.1.1.1.3. debugging
12.1.1.1.4. authorization
12.1.1.1.5. state control
12.1.1.1.6. add event
12.1.2. avoid redefinition of a property
12.1.2.1. Setting a variable directly gives control to outside code like
12.1.2.2. code
12.1.2.3. Solution
12.1.2.3.1. So, using getters and setters will give the control with our class instance and not other possible bad class.
12.1.3. easy relocation of data
12.1.3.1. Maybe in the original code, accessing data directly could be ok
12.1.3.2. However, based on requirements you may want to relocate the data to a database, file, or cache
12.1.3.2.1. if you access the data directly this will be very difficult
12.1.4. Legacy codes without setter and getters are extremely unmaintainable
13. abstraction / interfaces
13.1. Why we do abstraction?
13.1.1. enable some well know patterns
13.1.1.1. plugin systems
13.1.1.2. Decorator
13.1.1.3. chain of responsibility
13.1.2. to ship first and develop later
13.1.3. TDD
13.1.3.1. Write test first and implement later
14. dependency control
14.1. god objects
14.2. object drilling
14.3. dependency injection
15. domain driven design
15.1. uncle bob clean architecture
15.1.1. objective
15.1.1.1. the separation of concerns
15.1.1.1.1. this is done by dividing the software into layers
15.1.2. The Dependency Rule
15.1.2.1. The concentric circles represent different areas of software
15.1.2.1.1. The outer circles are mechanisms
15.1.2.1.2. The inner circles are policies.
15.1.2.2. This rule says that source code dependencies can only point inwards.
15.1.2.2.1. Nothing in an inner circle can know anything at all about something in an outer circle
15.1.2.2.2. data formats used in an outer circle should not be used by an inner circle
15.1.2.3. LAYERS
15.1.2.3.1. Entities
15.1.2.3.2. Use Cases
15.1.2.3.3. Interface Adapters
15.1.2.3.4. Frameworks and Drivers.
15.1.3. Styles
15.1.3.1. bad
15.1.3.2. good
15.1.3.3. not as good
15.1.3.4. not as bad
15.1.3.5. important
15.1.3.6. warning
16. tdd
16.1. why
16.1.1. automated test
16.1.1.1. especially, for multiple instances of same class
16.1.1.2. plugin systems
16.1.1.3. Sometimes, it is faster to test a functionality by automated tests than doing it manually
16.1.2. catch bugs that are extremely difficult to create manually
16.1.3. refactoring
16.1.3.1. When changing the code, you do not create a time bomb
16.1.3.1.1. this means catch the possible bugs by tested that where developped before
16.1.4. Maintenance
16.1.4.1. Passing to other engineers
16.1.5. Performance testing
16.1.5.1. Execution time testing