Skip to content

Commit

Permalink
Corrected spelling on architecture / part 1 to 5
Browse files Browse the repository at this point in the history
  • Loading branch information
Cosmeeeen committed Oct 23, 2020
1 parent fc854e3 commit 48d2ffc
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 84 deletions.
16 changes: 8 additions & 8 deletions architecture/clean-architecture/part-1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ When you write software right, you don't need gigantic issue tracking systems or
When software is written right, you need only a handful of developers to maintain it. Otherwise, you need a hoard of them to maintain it.

## What is design and architecture?
Design == Architecture. Design implies focus on low-level structuring details. Architecture implies focusing on higher-level components divorced from the low-level details.
Architecture is actually both as both are important for a good architecture.
Design == Architecture. Design implies a focus on low-level structuring details. Architecture implies focusing on higher-level components divorced from the low-level details.
Architecture is actually both as both are important for good architecture.

The goal of good software architecture is to minimize the human resources needed to build & maintain a project.

Expand All @@ -31,9 +31,9 @@ This is clearly disastrous as a company cannot sustain this cost for so long. At
From the developers' perspective, here's how their productivity looks like:
![Productivity per release](images/productivity-per-release.png)

Although they are working hard & doing overtime, their focus is slowly shifted from developing new features to managing the ever increasing mess.
Although they are working hard & doing overtime, their focus is slowly shifted from developing new features to managing the ever-increasing mess.

From the executive's perspective, this looks even more grim:
From the executive's perspective, this looks even grimmer:
![Monthly Payroll per Release](images/monthly-payroll-per-release.png)

### What went wrong?
Expand All @@ -44,7 +44,7 @@ The cleaning up, however, never comes.

Hence, the only way to go fast is to stay clean. There is no such thing as being fast in the short term and cleaning things up in the long term.

Example \w an acquintance of the author attempting to solve a popular problem using TDD & no TDD over a span of a week:
Example \w an acquaintance of the author attempting to solve a popular problem using TDD & no TDD over a span of a week:
![TDD vs. No TDD](images/tdd-vs-no-tdd.png)

> The only way to go fast is to go well
Expand All @@ -68,7 +68,7 @@ Good architecture provides the means to achieve that.
Bad architecture, on the other hand, makes engineering costs higher with every release.

### The greater value
Function or achitecture? Which is more important?
Function or architecture? Which is more important?

Clients will often say that function is more important than later flexibility.
However, that is the wrong attitude.
Expand All @@ -84,11 +84,11 @@ The behavior of the system is in quadrants 1 and 3. The architecture is in quadr
What business developers often make as a mistake is to promote problems in quadrant 3 to quadrant 1.
This way, the important bits of quadrant 2 never get the proper attention.

It is the software developer's job to stress the importance of architecture to the business & prioritise quadrant 2 over quadrant 3.
It is the software developer's job to stress the importance of architecture to the business & prioritize quadrant 2 over quadrant 3.

### Fight for the architecture
Fulfilling your duty of emphasizing the importance of architecture to the business is part of your responsibilities as a software developer.

This will inevitably lead to a fight with other stakeholders, but remember - you are also a stakeholder. A stakeholder of the code you write.
It is your responsibility to safeguard it & ensure that it grows with a sustainable architecture.
It is your responsibility to safeguard it & ensure that it grows with sustainable architecture.

26 changes: 13 additions & 13 deletions architecture/clean-architecture/part-2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ Notice that all these paradigms don't add anything to the programmer's capabilit

Paradigms tell us what not to do, rather than telling us what to do.

All these paradigms were discovered in the 1950s' and 1960s'. There were no major paradigms added afterwards.
All these paradigms were discovered in the 1950s' and 1960s'. There were no major paradigms added afterward.

## Structured programming
The paradigm was discovered by Edsger Dijkstra by analysing existing programs in an attempt to establish mathematical proofs for programs.
The paradigm was discovered by Edsger Dijkstra by analyzing existing programs in an attempt to establish mathematical proofs for programs.

What he discovered was that if a program uses simple constructs - sequence (e.g. `cmd1; cmd2;`), selection (e.g. `if/then/else`), iteration (e.g. `do/while`), such programs were provable.
However, if a program used `goto`s to do undisciplined jumps across function boundaries, these were very hard to prove.

Along the same time, some computer scientists proved that **all programs can be constructed from these simple structures**.
At the same time, some computer scientists proved that **all programs can be constructed from these simple structures**.

Hence, it was concluded that sticking to these three simple structures allowed one to construct any program he'd like, while also keeping his program mathematically provable.
This is how structured programming was born.
Expand Down Expand Up @@ -183,7 +183,7 @@ Additionally, this kind of inheritance relies on the fact that the member variab
OO languages get half a point for inheritance here as it is easier to use inheritance in those languages.
### Polymorphism
Polymorphism could be achieverd in C way before any OO language was invented.
Polymorphism could be achieved in C way before any OO language was invented.
It was achieved via the use of function pointers. Example definition of a old-style "interface":
```c
Expand Down Expand Up @@ -225,19 +225,19 @@ OO languages removed the need for these conventions as they've made the usage of
So that's the big breakthrough of OO languages.

### The power of polymorphism
The great power of polymorphism is that if in the future a new driver is developed, the program using the driver needs not be recompiled as it has a source code dependency on the `FILE` interface, rather than the specific driver implemeentation.
The great power of polymorphism is that if in the future a new driver is developed, the program using the driver needs not be recompiled as it has a source code dependency on the `FILE` interface, rather than the specific driver implementation.

In short, drivers are plugins to the UNIX OS.
This was necessary as we've learned in the 1950s that we want our programs to be device independent as otherwise, we would need to write a lot of device specific code which differs by little.
This was necessary as we've learned in the 1950s that we want our programs to be device-independent as otherwise, we would need to write a lot of device-specific code which differs by little.

Even though this was very convenient, programmers didn't use it that much as using function pointers was quite dangerous & best left to experts.
With the advent of OO languages, polymorphism has become something simple enough that anyone could do.

### Dependency inversion
In a typical software architecture, predating OO languages, the flow of control followed the source code dependencies:
In typical software architecture, predating OO languages, the flow of control followed the source code dependencies:
![Flow of control without dependency inversion](images/no-dependency-inversion.png)

This meant that any change in a low-level component required recompilation of the entire stack.
This meant that any change in a low-level component required the recompilation of the entire stack.

However, something very different happened when polymorphism is used:
![Dependency Inversion](images/dependency-inversion.png)
Expand Down Expand Up @@ -274,7 +274,7 @@ In clojure, it would be implemented like so:
(println (take 25 (map (fn [x] (* x x)) (range))))
```

What the above means is print the first 25 elements of a never-ending list, whose elements are evaluated as `x = x * x`.
What the above means is to print the first 25 elements of a never-ending list, whose elements are evaluated as `x = x * x`.

The big difference between the two implementation is that the java implementation relies on mutating a single variable `i`.
In the clojure variant, there is no mutability whatsoever. Every time an operation is performed, a new object is created.
Expand All @@ -284,7 +284,7 @@ In other words, elements are immutable by default.
### Immutability and architecture
The great benefit of immutability is that it saves us from all possible concurrency problems - race conditions, deadlocks, etc.

If variabls aren't mutated, there can't be any concurrency issues. This makes developing programs for multiple processors/threads much easier.
If variables aren't mutated, there can't be any concurrency issues. This makes developing programs for multiple processors/threads much easier.

Total immutability, however, is only practical if one has infinite storage and infinite processor speed.
Hence, in order for this to be practical, certain compromises need to be done.
Expand All @@ -307,10 +307,10 @@ Using such a scheme, there cannot be any concurrent update issues as there are n


## Conclusion
These are the three major sotware engineering paradigms ever invented.
These are the three major software engineering paradigms ever invented.

None of them adds anything to our capabilities, they only take away something from us.
Hence, realize that software is not a rapidly-evolving discipline. It has stayed the same ever since Alan Turing wrote his first line of code.

All programs consists of some permutation of sequence, selection, iteration and indirection. Nothing more, nothing less.
It is only our tools and knowledge of how to write software which has evolved.
All programs consist of some permutation of sequence, selection, iteration and indirection. Nothing more, nothing less.
It is only our tools and knowledge of how to write software that has evolved.
22 changes: 11 additions & 11 deletions architecture/clean-architecture/part-3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ The executive summary of the principles is:

## The Single Responsibility Principle

SRP is the least well understood principle as it is very easy to confuse it for:
SRP is the least well-understood principle as it is very easy to confuse it for:
> A module should do exactly one thing
That's not what SRP is about. The official definition is:
> A module should have one, and only one, reason to change
A "reason to change" refers to the user or stakeholders which might want to change something.
A "reason to change" refers to the user or stakeholders who might want to change something.
However, users & stakeholders aren't a good formal definition, so a better word would be "actor":
> A module should be responsible to one, and only one, actor.
> A module should be responsible for one, and only one, actor.
The reason for us to want to adhere to this principle is to make sure that if different teams/people working on different tasks don't modify the same classes/modules.
If we let this happen, there is a possibility that when someone makes a change in a class to satisfy stakeholder A, it might accidentally make changes to the behavior demanded by stakeholder B.
Expand All @@ -58,7 +58,7 @@ With such a structure, here's an example bad scenario:

`reportHours()` and `calculatePay()` use the same shared function `regularHours()` which mandates the calculation of a user's regular hours.

If the HR department want to change the way the regular hours are calculated, one might go to this class, change the shared function, carefully test it and satisfy the new requirements laid out by HR.
If the HR department wants to change the way the regular hours are calculated, one might go to this class, change the shared function, carefully test it and satisfy the new requirements laid out by HR.

However, since this function is also used by the `calculatePay()` method, which is used by accounting, you've accidentally changed the way regular hours are calculated from an accountant's perspective, resulting in wrong documents, which can lead to huge liability risk and/or incident expenses.

Expand Down Expand Up @@ -89,7 +89,7 @@ If small extensions in requirements force massive changes throughout the codebas
The purpose of this principle is to guide you in designing your system in a way that extensions to requirements can be satisfied by adding additional code, not by changing existing code.

### A thought experiment
Say we are given a system that displays financial summary on a web page.
Say we are given a system that displays a financial summary on a web page.
Stakeholders come and want us to extend the system to support printing the same info on a PDF to be printed.

A good architecture would allow this to be achieved by only adding additional code, not changing existing code.
Expand All @@ -98,7 +98,7 @@ A side-note here is that, of course, some code will have to be changed - e.g. th
This can be achieved by properly separating things that change for different reasons (SRP) and properly organizing dependencies between these components (DIP).

The provided solution is:
![System comforming to OCP](images/ocp-system.png)
![System conforming to OCP](images/ocp-system.png)

The main insight is that the classes responsible for calculating the data need to be different from the classes displaying it.
Additionally, the source code dependencies need to be organized in a way that changes to one of these responsibilities doesn't cause changes in the other one.
Expand All @@ -109,7 +109,7 @@ The full design of the system:
One thing to notice is that all dependencies between components are unidirectional:
![Simplified system design](images/distilled-ocp-system.png)

The higher-level components (e.g. Interactor) know nothing about the lower level components which implement the higher-level interfaces.
The higher-level components (e.g. Interactor) know nothing about the lower-level components which implement the higher-level interfaces.
If we want to protect a component A from changes in component B, then component B should depend on component A, not the other way around.

In this diagram, the Interactor is the highest-level component, which holds the business rules. It should be protected from changes in any other component.
Expand Down Expand Up @@ -145,7 +145,7 @@ The only way to guard themselves, the user has to type-check whether the `Rectan
Since the behavior of the user depends on the types they use, these types are not substitutable.

### LSP And Architecture
In the beginning, LSP was used as a guiding principle on designing subclasses.
In the beginning, LSP was used as a guiding principle for designing subclasses.

With time, it also became acknowledged as a principle applicable to high-level software design.
The interfaces in question, need not explicitly be (e.g.) Java interfaces. They can be a set of classes conforming to a REST API.
Expand Down Expand Up @@ -180,7 +180,7 @@ This is problematic because a change in `op2` and `op3` would force `User1` to r
If this is segregated into interfaces, this is how the diagram looks like:
![ISP Good Example](images/isp-good-example.png)

Note that there is still only one class which implements all three operations, but now, each user only depends on the interface they need.
Note that there is still only one class that implements all three operations, but now, each user only depends on the interface they need.
Hence, a change in `OPS`, which `User1` doesn't care about won't force `User1` to recompile & redeploy.

### ISP and Language
Expand Down Expand Up @@ -226,7 +226,7 @@ Typically, those are the custom modules created in our project.
The reason for following this principle is that abstractions tend to be stable & rarely change.
Concrete classes, on the other hand, change quite often but rarely do those changes cause changes in their interfaces.

This is why, depending on abstractions will lead you to create more stable components which change less often.
This is why, depending on abstractions will lead you to create more stable components that change less often.
At the other extreme, a change in a low-level module might propagate changes in all other modules in the system.

This principle boils down to a very specific set of coding practices:
Expand All @@ -250,7 +250,7 @@ This is why this principle is referred to as dependency inversion.

### Concrete Components
Although DIP is applied here, there will be a necessity for a module which instantiates `ServiceFactory` to `ServiceFactoryImpl`. This is inevitable and expected.
However, we should keep the amount of such components as low as possible. Typically, this wiring is done in the main function.
However, we should keep the number of such components as low as possible. Typically, this wiring is done in the main function.

This can also be achieved by relying on a dependency injection framework.

Loading

0 comments on commit 48d2ffc

Please sign in to comment.