Author: Chuck Conway

Uncoupling – Micheal Nygard

Coupling is the difficulty when changing things.

Tensegrity – Holds it self together by the tension among parts and the resistant tension from the beams. It’s a nest of opposing forces.

Coupling is everywhere. We are coupled to the Earth. The Earth is coupled to the Sun. The solar system is coupled to the Milky Way. The same is true if we go to the atomic level.

Coupling defines the degrees of freedom. It constrains and enables some movement. It also connects effects.

Everything is coupled to everything

  • ambient temperature
  • ambient atmosphere
  • acoustic vibrations
  • Electromagnetic field
  • Gravity
  • Higgs field
Type Effect
Operational Consumer cannot run without the provider
Development Changes in producers and consumers must be coordinated
Semantic Change together because of shared concepts
Functional Change together because of shared responsibility
Incidental Change together for no good reason (e.g., breaking API changes.)

A shared concept is an idea that is exposed/implemented in one of area of the system. And then other consumers (external or internal) must embrace the concept to consume the resource. However, if the concept is flawed, that flaw proliferates throughout the system.

System Diagrams – Long Arrows

  • Arrows connecting two systems is actually hiding a number of moving parts
    • Extract tables to files
    • Push files across network
    • Load data into database
    • etc…
  • Architecture Qualities in Long Chains
    • Latency strictly warse than the slowest link in the chain
    • Availability strictly worse than the least available link
    • Throughput strictly worse than the throughput of the worst bottleneck
    • Security strictly worse than the security of the weakest link
  • Digging Out – Information Hiding (Decision Hiding)
    • On the Criteria to be used in Composing Systems into Modules – David Parness

Composing Interfaces

A smaller number of well defined interfaces is preferable over more interfaces and small number of concise datatypes is preferable over more datatypes.

Composiblity

  • Inversely proportional to the # of interfaces
  • Inversely proportional to the # of data types

Uncoupling

Orthogonality in Software

  • separation of concerns
  • High cohesion within a module or component
  • low coupling between modules or components
  • little overlap in functionality between modules
  • information hiding / decision hiding

Rotate your perspective 90%

  • When looking at components, think about modules
  • When looking at modules, think about components
  • When looking at data, think about code
  • When looking at code, think about data

Use all your tools

  1. Module structure – layout of your code and libraries
  2. Component structure – interactions between runtime components
  3. Abstraction – Emphasize similar interfaces & data formats

O’Reilly’s Software Architecture – Domain-Driven Design

O’Reilly Learning Course:

Domain Driven Design – Software Architecture

Andrew Harmel-Law

The Heart of Domain-Driven Design

Complexity in Software Tackling complexity is the heart of software. Complexity is in the problem domain, not in our software. We add our own complexity in solving the problem that may or may not be needed. This additional complexity is on us.

Concepts of that are Core to Successful Domain Driven Design

Ubiquitous Language

Domain Experts are developers who can’t code and we are developers who don’t know the domain.

Translation fractures the language between the developer and the business. It’s a mental overhead and things are always lost in translation. If we (the engineering team) and the business have the same understanding and use the same terminology there is no overhead to communication concepts. There is no opportunity to lose meaning in translation. What the point of using two different terms to mean the same thing? Isn’t this idea much like copy and pasting code? They might be the same to start with, but they always diverge over time?

Why are accounts in the banking domain opened and not created?

Respect the domain experts, the domain might seem simple, but once you start talking to the domain experts, it’s not. Domain experts have the solution to one of the hardest problems in Software Development and that’s they names for all the concepts.

Domain experts have a term that succinctly communicates a complex idea?

Are they correcting your word usage?

If so, these are the hints of a complex ideas and the naming to got along with it.

Hands-On Modelers

Developer assumptions get shipped to production and sometimes these assumptions are wrong.

In a typical organization the business domain is documented by a Business Analysis, which records what they think the developers will need. This document is then handed to the Developers. The Developers are reading the Cliff Notes version of the domain problem. This may or may not be the details the developers need to complete their work. Getting the Developers as close to the source of information gives the highest chance of getting the software right.

Engineers working on the model must understand the change they are making and how they are improving the model. If they don’t understand this then they are weakening the model.

Tests verify the facts of your Ubiquitous Language are still true.

Procedural and Data-Centric don’t work for DDD.

Breakthroughs (fine tuning the Ubiquitous Language)

Focus on breakpoints on the Ubiquitous Language. ARea’s of the domain/model that break down and don’t meet the needs of the problem domain.

Tests don’t do this because most of the time you are testing the positive of the assumptions.

Multiple Models

"Bounded Contexts"

A bounded context is an area of the problem domain that had natural or logic borders from other idea’s in a system.

DRY (Don’t Repeat Yourself) doesn’t apply across Bounded Context boundaries. If you do, you are coupling these two Bounded Contexts. The fact that they are two bounded context means they are two different spaces and shouldn’t have overlap. Sharing algothrims is coupling.

If there are truly algorithms that span Bounded Contexts, they should be cross-cutting concerns. For example, working in the financial industry and using financial calculations in multiple Bounded Context is an example of Cross-Cutting concerns.

DRY isn’t about code, it’s not about removing code duplication, but removing duplication of knowledge.

Nested Bounded Contexts aren’t a thing.

Do’s and Don’ts of Error Handling – Joe Armstrong

Systems will fail. You can’t have a resilient single system. Even if internally, the system is rock solid, it can’t contend with all outside variables and will at some point succumb to outside forces.

The basis of object-oriented programming is message passing. If you have two systems that work together, they must pass message between each other. There must be a mechanism to share data. In some systems this is a Database, a Queue, or an API.

  • Some small things can be proven to be self-consistent
  • Large assemblies of small things are impossible of prove correct.
  • Scalability is impossible on one computer
  • Security is very difficult with one computer.

Arithmetic Errors

  • silent and deadly errors – errors where the program doesn’t crash but delivers incorrect results
  • noisy errors
  • errors that cause the program to crash

Silent Programming Errors

  • Why is it silent, because the programmer doesn’t know there is an error.

Rump’s Royal Pain

Rump’s Royal Pain

[Fact]
public void RumpTest()
{
    int x = 77617;
    int y = 33096;

    var value = 333.75 * Math.Pow(y, 6) + Math.Pow(x, 2)*(11 * Math.Pow(x, 2) * Math.Pow(y, 2) - Math.Pow(y, 6) - 121 * Math.Pow(y, 4) - 2) + 5.5 * Math.Pow(y, 8) + x / (2 * y);
}

Domain Driven Design Notes

Why DDD?

YAGNI – You are not going to need it.

  • Shortening development time

KISS – Keep it short and simple

  • Maintainable

DDD

  • Focus on essentials parts
  • Simplifying the problems.

We can only handle so much complexity in our brains.

Ubiquitous language

  • Use the same language between the coders, QA, BA, Clients

Bounded Context

  • Separate area’s of the system that don’t make sense to be together.

Core Domain – Focus on the most important part of the system. Think of Core Competency in business

When to unit test Where Test Coverage meets value. At some point, adding tests doesn’t add value. I.e. principle of diminishing returns

Types of Equality Identifier equality – do the Id’s between the entities match? Reference equality – do the two objects point to the same reference in memory Structural equality – do the two objects have the same values.

Entities vs. Value Objects

  • Entities
    • Have inherent Identity
    • Reference equality
    • Identifier equality
    • Single place for equality members
  • Value Object
    • Value objects don’t have an identifier field.
    • Can be treated interchangeably
    • Immutable
    • Can’t live on their own, must belong to one or several entities

How to Recognize a Value Object

  • Value objects are light-weight
  • Put most of business logic to value objects
  • Entities act as wrappers

When to create a Repository Repositories are generally created for each aggregate root.

Null Object Design Pattern

Command Query Separation Principle

Anti-Corruption Layer

Bounded Context is a logic separation of the domain in the code. Some examples of this in use is putting all the code related to a Bounded Context under the same folder or using different schemas in the database. Yet, another possibility is different deployments.

O’Reilly’s Software Architecture Series

O’Reilly Learning:

Software Architecture Superstream Series

Trisha Gee

Anti-Patterns

Bad Code Reviews

A bad code review is a review that makes the person feel bad about the code they just spent hours working on.

We are human beings; instead of saying this is wrong, offer reasoning and suggest another direction.

Code reviews are about learning, collaboration, and conversation to improve the product, the company, and the people.

Book Recommendations

  • Delusions of Gender: How Our Minds, Society, and Neurosexism Create Difference
  • Programmed Inequality: How Britain Discarded Women Technologists and Lost Its Edge in Computing
  • Invisible Women: Data Bias in a World Designed for Men
  • 97 Things Every Java Programmer Should Know
  • Head First Design

Mark Richards

Software Architecture Styles

Architecture Styles vs. Architecture Patterns

Architecture Styles is a general strategy to an architecture. One or more architecture patterns can be used within an Architecture Style. Architecture Styles may also be hybrids. For example, an event-driven architecture may be made up of small components and with their data sources and deployed independently. This would be a microservice event-driven architecture.

Sonya Natanzon

The Role of the Software Architect

The Three Skills Architects must have

People

The architect must be an influencer. Value proposition, explaining the solution, and selling your ideas.

Conway’s Law. Org structure influences application structure. The lines of communication influence the application structure.

Growing new leadership. A lot of the time, this means training people.

Knowledge sharing.

Delegate to others and support others as they work through the problems. You can’t take on all the work yourself.

Create room for experimentation and have tolerance for mistakes.

Technical

Always learning new technologies and ideas. And then teaching and knowledge sharing. Information hoarding doesn’t work.

Selecting a toolbox. What technologies should our company use to be successful?

4 C’s

Analytical Skills

Thought

Evolve the Big Picture – Understand the path to how we got here and then chart a path forward. Create a roadmap for the next 6 months to a year.

Champion Process – People don’t fail you; the process does. This means having code reviews, retrospectives, following best prates. The process serves the team, not the other way around. Always be looking for ways to eliminate waste, whether this means adding process or removing it.

Set Culture and Values – Build, change, and influence culture and values.

The Four Immeasurables

Near Enemy and Far Enemy

  1. Loving Kindness
  2. near: selfish love
  3. far: ill will
  4. Compassion
  5. near: pity
  6. far: cruelty
  7. Empathic Joy
  8. near: blind euphoria
  9. far: jealousy
  10. Equanimity
  11. near: indifference
  12. far: paranoia
Near Enemies of Software Architect Attributes
  1. People
  2. near: functional management without influence means you become a taskmaster. You don’t have buy-in from your team.
  3. Tactical
  4. near: Individual Contributor rarely leaves time for a broader strategy. You don’t have the time to step back and see the big picture.
  5. Thought
  6. near: Ivory Tower Architect – They make architecture an academic exercise without real and practical value. They get hung up on the "right way" instead of being pragmatic and shipping software.

Solution Process

Problem Definition

  • problem statement
  • supporting analysis
  • as-is documentation

Solution Proposal

  • Models diagrams and other documentation
  • Technology selection
  • POC’s
  • Ubiquitous language definition

Implementation

  • API Definition, schemas, data models
  • production code
  • changes to everything are done in the previous phases.

Enhancement

  • Changes
  • Bugs
  • Maintenance

Book Recommendations

  • The Five Dysfunctions of a Team: A Leadership Fable – Patrick Lencioni
  • Multipliers: How the Best Leaders Make Everyone Smarter – Liz Wiseman and Greg McKeown

Nate Schutta

Thinking Architecturally

We can’t predict the future; we must decide on something concrete now and stick with it. We can’t be chasing the future. We also must plan for the future and move to technologies when it makes sense for the business. That might be because it gives us a competitive advantage or the cost of supporting our existing platform is rising.

In software, we don’t know. It’s about trial and error. Let’s try it and find out.

Every single choice has trade-offs. When someone comes to you and says, "This is the most amazing thing; it’s going to solve every problem we’ve ever had," you should immediately ask, "What are the trade-offs?" This is design, in a nutshell, this is architecture in a nutshell.

There are three answers to every computer science questions

  • 42, this is the geek check to see who is in the know and well-read
  • Another layer of indirection
  • it depends

Balancing when to use what tool over the other is the job of the architect. There is no perfect technology.

When presenting new technologies, you should be able to answer the following questions:

  1. What do you like about this technology?
  2. What DON’T you like about this technology? — you should have spent enough time with this technology to understand its shortcomings. If you haven’t, why are you recommending it?

"Quality Requirements" is another way to say the "ilities", for example, Scalability, Reliability, Simplicity.

There are two ways to sell you the idea:

  1. The hammer – I’m going to tell you this is a good idea
  2. The ninja – I’m going to convince you that it’s your idea and it’s a good one.

Sam Newman

Microservices

The Fallacies of Distributed computing apply.

Monolith does not mean a big bad legacy system. It’s just a bigger deployment package with multiple services/domains that share their data.

Fundamentally, Microservices are a type of distributed system.

Any distributed network had the concerns of latency.

You have the concerns of consistent data. Microservices embrace this idea of a single source of truth.

One of the things that set Microservices apart from other services is it does not share its data. It owns all its data. It’s truly an encapsulated service, including its data. There is no big shared database on the backend.

The problem of partitioning is when one service can’t see another service. This happens there is a disruption in the network.

The opinions of Microservices is that each service is independently deployable and that data that mutates is stored within a service boundary.

Microservices is end-to-end functionality modeling around the aspect of the business domain.

Microservices is the first post-DevOps system.

The Monolith is not the enemy. The Monolith is not the enemy; it’s not bad. A monolith is just a different style of architecture.

Microservices is typically not a place to start but a place to end. Start out discovering your business domain by building a modular monolith. When you’ve reached your current architecture limits, and Microservices is a good fit, move to a Microservice architecture.

Not all of your services need to be Microservices.

Microservice size is not about code but about the business domain, functionality, and data. It’s also about how easy is it to create a service? The easier a service is to create, the more and the smaller the services will be.

Start off with one or two services, then add more services as you are successful with the first services. Adding microservices is not an on/off switch but a dial was to increase the volume.

Stick with, you know. Don’t overload yourself with things to learn. If you know, Go, stick with it.

The only prerequisite to Microservices is Log Aggregation.

Data is accessed and mutated via the Microservice, not directly at the data source.

Microservices is a style of modular architecture. It’s data encapsulation at a service level.

Microservices is about functional slicing at the team and architecture level. This includes the UI level also.

Give a Safe Space to Express Ideas

When leading a team, it’s important to create an environment where everyone feels safe to express their ideas regardless of their experience level.

Early in my career, I was leading a team of six. One of the software engineers approached me with an idea; I knew it wouldn’t work. Instead of telling him, he’s wrong. I told him to set up a meeting with the rest of the team.

In the meeting, the team agreed his approach wouldn’t work, but they took aspects of his solution and incorporated them into the final solution. If I had told the software engineer no from the get-go, the engineer wouldn’t have felt heard, and we would’ve had a less robust solution.

Scrum is Overrated

Most companies follow some type of Scrum process. Typically this entails 2 or 3 week sprints. At the end of each sprint changes are demoed, retrospectives are performed and the backlog is groomed. During each sprint task completion time is captured, which allows management to project into the future when projects will reach completion.

Many of the Scrum projects I’ve been apart of emphasize “committing” to tasks or “taking ownership.” At the end of the sprint many engineers are held accountable for incomplete tasks. Sprint velocity is another idea that is hammered home. We have to keep our velocity! It’s like creating software is a race, it’s not. If engineers are held accountable by a metric, they’ll optimize for the metric, this isn’t want you want.

Scrum creates an easy to understand framework for teams to follow and it gives management the tools to predict the future. Teams that have practices waterfall find Scrum easy to grok.

Many of Scrums practices aren’t needed. For example, most issue tracking software allow managers to run reports on the frequency of ticket completion. With this information, managers are able to infer velocity, instead of baking velocity into the process and making it a big deal. Taking ownership is a farce, we do it naturally, to make it explicit is insulting. All the projects I’ve been a part of each engineer has a corner of the application that’s their space.

Other ways to improve software delivery:

  • If you need weekly deployments, schedule them. Deploy what’s ready.
  • Keep the backlog groomed; then engineers never run out of work.
  • In my opinion, retrospectives are the most essential non-development activity. Without it, you have no chance of becoming a better and more efficient organization.
  • Automate, automate, automate
  • Committing to a list of features is ridiculous. Rank the tasks and complete what you can. Fretting over why “task A” wasn’t complete is a waste of time. It’s clear the task was either too big, or higher priority work was taken on.
  • Demos are a waste of time unless the client cares and provides feedback. 
  • Daily meetings may or may not be needed. I prefer meeting every couple of days.

At the end of the day it’s about providing value to the client in the most efficient way.

Writing Allocation Free Code in C# By Matt Ellis

Twitter: @citizenmatt
GitHub: https://github.com/citizenmatt/RefSemantics

Always be measuring – don’t do it because you think it’s the right thing to do. It might not be.

Managed memory is cheap. That’s not true, allocation is cheap and garbage collection is expensive. Garbage collection stops the world while things are cleaned up and optimized. Sometimes memory cleanup can impact the performance of the application.

Performance Low Hanging Fruit

Object reuse

Object pooling. Pass in existing array rather than allocating a new one. Preallocate array length.

String concatenation.

Use StringBuilder, Preallocate length if possible.

params arguments

void MyParamsMethod(params string[] args)
{

//... 

}

In Code:
MyParamsMethod("Hello", "World");
MyParamsMethod();

Complier:
MyParamsMethod(new {"Hello", "World"});
MyParamsMethod(new [0]); // this creates a new array object in memory

Instead, do this in code:
MyParamsMethod(new {"Hello", "World"});
MyParamsMethod(Array.Empty); // reuses an existing empty string array in memory. It's the same idea behind string.Empty

Suggestion: Introduce overloads with common number of arguments.

Boxing

Boxing creates a new object on the heap. Now you have two memory locations with the same value. Changing either value does not impact the other value.

Suggestion: Introduce generic overloads

Closures

The complier converts closures to classes. Captured values are passed in as constructor parameters. This class is then allocated to the heap.

Suggestion: Avoid critical paths. Pass state as argument to lambda. Investigate local functions. Since local functions lifetime is known, it can be allocated on the stack where allocation and cleanup are cheap.

LINQ

Lambda expressions are treated the same as Closures. They are allocated as a class to the heap. Because much of LINQ is based on static methods, additional allocations of Iterators and IEnumerable happen.

Suggestion: Avoid critical paths. Use good old foreach and if statements

Iterators

iterators are rewritten as a state machine, which means more allocations to the heap.

Suggestion: Return a collection. Be aware of the cost.

async/await

Async/await also, generate a state machine. Task and Task also trigger more allocations which then can’t be reused.

Look into ValueTask for common uses cases.

Suggestion: Investigate ValueTask.

Heap Vs Stack

Each method pushes space onto the stack for local variables. When the method is exited the memory is popped from the stack.

Stack allocation and cleanup are cheap because the memory has a lifetime. However, stack space is limited.

*Why passing value types copies the data versus passing a references it’s important to note that copying data isn’t that expensive.

Reference Semantics with Value Types

Allows value types to be used like reference types
– pass by reference everywhere

User value types to reduce allocations, reduce memory traffic, etc
– Throughput!

Pass by reference to avoid copies, enables modifying, etc.

Very low-level micro-optimizations…
– But they’ll be used in the platform…
– (And games, and parsing, and serialization, and …)

*These are low level optimizations that should only be used in paths were performance matters.

C# 7.2 Reference Semantics with Value Types

Allocating a reference type has a cost, but passing it around is cheap.
Allocating a value type is cheap, but passing it around has a cost.

Why can’t it be cheap to allocate AND cheap to pass around?

in parameters

Pass value type by reference. Called methods cannot modify it.

Method argument modifier

Complements out and ref

Passed by reference

Method cannot modify original value

Compiler enforces safety with defensive copy when calling members

ref locals and ref returns (C# 7.0)

ref returns

Returns a reference to value the, not a copy of the value.

– return type of method becomes e.g. integer reference int& in IL

Lifetime of returned value must exceed the lifetime of the called method

  • e.g. a reference to a field or method argument. NOT a variable in the called method.
  • Not allowed on async methods.

Modifying this reference is the same as modifying the original value

  • e.g. return reference to array element, and update it in place.

Add ref modifier to method declaration return type, and to return statement

ref locals

Assign a ref return to a new variable will create a copy

  • The variable is a value type, not a reference. (Cannot assign int& to int)
  • A ref local is a variable that is a reference to a value type
  • Accessing the variable accesses the original value

Use a ref local to store the ref return result

Type inference with var will get the value type, not the ref modifier

  • Requires ref var to work as expected.

ref readonly returns

Returns a read only value type by reference

readonly struct

Immutable value types

in parameters and ref readonly can create defensive copies

  • The compiler doesn’t know if the struct’s methods will modify state

readonly struct – compiler enforces all fields and properties are readonly

Immutable

More efficient – no copies made when calling members

  • Improves performance (micro-optimization)

ref struct

Stack only value types

Declare a value type that can only be stack allocated

  • I.e can never be part of a reference type

This constrains lifetime to calling method

  • Also, cannot be boxed, cannot use inside a non-ref struct
  • Cannot use with async methods or iterators
  • Cannot be a generic parameters

Limited use cases

  • Working with stackalloc memory
  • Primarily for Span<T>

What does SPAN have to do with ref struct?

For thread safety, need to update all fields of Span atomically (tearing)

  • Whole point is performances – cannot use synchronization

Internal pointers require special GC tracking

  • Too many in flight at once is expensive

How can SPAN represent stackalloc memory is SPAN was on the heap?

Solution: Span<T> is a ref struct – can only be created on the stack

  • Constrained lifetime, single thread access

Span<T>

New type of unify working with any kind of contiguous memory

  • Arrays, array segments, strings and substrings, native memory, stackalloc, etc

Provides array-like API – indexer

  • ReadOnlySpan provides getter indexer only

Type safe – each elements is of type T

Array-like performance

  • Not quite, but newer runtimes have special support

Slicing

  • Create a new Span with a sub-section of existing – without allocations!

Span<T> Implementation

Value Type – struct

System.Memory NuGet package

  • .NET Standard 1.1 (.NET Framework 4.5)+

New APIs and overloads in the BCL

  • E.g. String.AsSpan(), Stream.ReadAsync(), Utf8Parser.TryParse()
  • Significant usage of ref semantics – allocation free!

Span, ReadOnlySpan, Memory

Two versions – “portable” and “fast”

  • fast requires runtime support

Span<T> Performance – Portable Implementation

Portable works on .NET Standard 1.1 and above

  • .Net Framework 4.5+

Portable is not slow

  • But not as fast as arrays

Three fields – object reference, internal offset and length

  • Slightly larger than fast version, dereferencing is slightly more complex operation.

Span<T> Performance – Fast Implementation

Fast requires runtime support

  • .Net Core 2.1

Only has two fields – “byref” internal pointer and length

  • Slightly smaller struct and accessing an element is slightly simpler operation

Specific JIT optimizations

  • e.g elding bounds check in loop, like arrays

Very close to array performance

A Binary Search Implementation

The binary search algorithm quickly searches a large array of numbers, it’s often referred to as divide and conquer.

public class BinarySearch
{
    public int BinarySearch(int[] items, int searchValue)
    {
        int left = 0;
        int right = items.Length - 1;

        while (left <= right)
        {
            var middle = (left + right) / 2;

            //If the searchValue is in the center, we found it!
            if(items[middle] == searchValue)
            {
                return middle;
            }            

            //If the searchValue is less than the current middle, we set the right to (middle - 1)
            //Because the searchValue is in the lower half of the items.
            if(searchValue < items[middle])
            {
                right = middle - 1;    
            }
            //If the searchValue is greater than the current middle, we set the right to (middle + 1)
            //Because the searchValue is in the higher half of the items.
            else
            {
                left = middle + 1;
            }

        } // now that we've either found the item and returned it or we've reset our search boundaries
          // we'll search it again.

        // Not found.
        return -1;
    }
}

The Benefits of Using a Build Framework

Continuous Integration (CI) and/or Continuous Delivery (CD) is the norm on software projects these days. There are many build servers such as Azure DevOps, TeamCity, Jenkins, and Cruise Control.Net. Most of these servers use proprietary languages to define build steps. But is codifying your build steps in a proprietary language a good thing?

Some applications are simple, with a few build steps, others are more complex with many build steps. When you define build steps in a proprietary language, the more complex the build steps (in sophistication or in number) the more coupled to a build platform you become. This becomes an issue when you want to switch build platforms. For example, you’re using JetBrain’s TeamCity in your on-premise datacenter, but the company decides to move to the cloud. Now you must re-write your build scripts because TeamCity isn’t supported in the new cloud platform.

Instead of writing your build scripts in a proprietary language, consider using a build framework.

Build frameworks have two benefits:

  1. Allowing transportability between build platforms.
  2. Allowing you to version your build scripts alongside your application code.

Transportability between platforms gives you the flexibility of moving between build platforms with minimal effort. There will always be some configuration on a new build platform, but build frameworks keep the effort low.

In my opinion, the biggest benefit to build frameworks is the ability to check-in and version your build scripts alongside your application code. Having the option to pull code from any point in your source control’s history and having that code build is well worth any downsides of a build framework.

There are two popular frameworks in the .Net space: Cake and Nuke Build. Both frameworks have been around for a while. I’ve used Nuke Build and enjoy it. I’ve heard great things about Cake and encourage you to look at it before deciding which is the best framework for your project.

So the next time you’re creating a new build definition for your application, consider using a build framework and checking it in source control with your application.