In this lesson we will discuss several tactics for modifiability, of which the goal is to control the complexity of making changes as well as the time and cost to make these changes. To understand modifiability tactics we begin with explaining coupling and cohesion. Modules have responsibilities and by making changes to the modules, these responsibilities are changed in some way. Generally, a change that affects one module is cheaper than a change that affects two or more modules. However, if the responsibilities of two modules slightly overlap a single change can impact them both. Coupling is the probability that a change to one module will propagate to another module. It is an enemy of modifiability. And we should reduce coupling as much as possible if modifiability is important. Cohesion measures how strongly the responsibilities of a module are related. It is the probability that a change scenario that affects on responsibility will also affect other, different responsibilities. The higher the cohesion, the lower the probability that a given change will affect multiple responsibilities. If we look at an example from an e-commerce website, the change in the module that is responsible for the payment should affect the modules that is responsible for user analytics. We want low coupling between these two modules. If we want to change the payment process, it should not affect the user's browsing analytics, so we want these responsibilities to have a high cohesion. There are four parameters we can affect to make our system more modifiable. The first one is the size of the module. If the system is made up from several smaller modules It will be easier to make the change than if it was just one big module. The next one is cohesion. If a module has low cohesion, this can be improved by removing responsibilities until the cohesion is high enough. Reducing the coupling between different modules will help us to assure that a change to one module does not propagate to another module. The last parameter is the binding time of the modification. This last on is often overseen, or considered trivial. But it can have a very big impact on the modifiability of a system. If you want to make your system highly modifiable, you want to bind things as late as possible. For examples, if you use three services to calculate the value, you might want to use four or more values later on. If the three services are defined in the code, you need to adapt to code, rebuild and redeploy a new version. If the services are not described in the code, but in a separate settings file, you only need to replace that file. This can be very important in environments where deploying is a cumbersome task. Such as on mobile devices. On Android, you cannot force users to install the latest version of the application. So you should avoid a redeploy as much as possible. The different tactics fall under these four categories. The tactic split modules defines that larger modules with lots of capabilities should be split in separate smaller parts that separate these capabilities. Increased mental cohesion tells us that if a module has two responsibilities that do not serve the same purpose, these responsibilities should be split across separate modules. For example, responsibilities that deal with hardware should be allocated to the hardware module and not to the application level. A hardware responsibility typically does not have any semantic coherence with application responsibilities. There are a couple of tactics that help us to reduce coupling between modules. Encapsulation introduces and explicit interface to a module which includes an application programming interface and it's associated responsibilities. It hides information about the system behind this clearly defined interface. If later on any changes to the system are made, the interface can stay the same and hide the internal changes. So that no changes need to be made to the other services that use this interface. However, interfaces need to be well designed since they can introduce limitations as well. They should be designed in an abstract way that increases modifiability. The tactic using intermediaries breaks dependencies between several modules. The type of intermediary depends on the type of dependency which can be either a data dependency or a service dependency. The data repository such as a database acts as an intermediary between the producer and consumer of data, If This Then That, an online platform that lets you connect different applications, acts as a service intermediary between several systems and translates actions and data from one system to the other. Restrict dependencies is a tactic that restricts the number of modules a given module interacts with or depends on. In practice, this is achieves by restricting a module's visibility and by authorization. This tactic is seen in layered systems. Where a layer is only allowed to use lower layers, and sometimes even only the next lower layer. Refactor is a tactic undertaken when two modules are affected by the same change, because they are at least partially duplicates of each other. Common responsibilities are separated fromboth modules and are based in separate module to reduce the coupling. This tactic is often applied when you create architecture in an incremental way. After each set of changes you might want to refactor the architecture and the code to make sure you're not duplicating functionality and responsibilities. In a situation where similar services are needed, you can often implement them only once in a slightly more general form so they can be reused by others. This tactic is called abstract common services. An example of this is a location service that GPS and offer, and that are used by thousands of other applications. The last tactic for modifiability is called defer binding. When we bind parameters at a later time, it gives us more flexibility in later stages. A parameterized function f with parameters a and b is more generic than a function with parameter a that assumes that b is 0. The first function can execute its action equally good as the second function, but allows more flexibility if you want to change b to 5. In general, the later in the life cycle we combine values, the better. Values, parameters, or components can be bound at implementation or design time, at build or compile time, at start-up or initialization time, or at runtime. Examples are replacing components in a build scripts ad build time, using resource files to load the right language at initialization time. Or, to use plugins to add functionality at runtime. This was a summary of the different tactics that can be used to make a system modifiable. Each quality attribute has it's own sets of tactics that can be used.