Developing with Agility
What this talk is not about
- Anti-waterfall
- Agile™
- Scrum
- Kanban
- Any other project
management
marketing terms
Agile™ Software Development
Coined by consultants
Scrum & Kanban are not bad or irrelevant; they provide frameworks for organizations
Not a requirement for developing with agility
Agile is not a noun. You can't do agile.
Agile Manifesto
No mention of sprints, backlog, etc
Our consultants did not mention the manifesto 18 months ago
Focus of this Talk
-
Welcome changing requirements, even late in development. Agile processes harness change for the customer's competitive advantage.
-
Continuous attention to technical excellence and good design enhances agility.
-
Simplicity--the art of maximizing the amount of work not done--is essential.
- Agile processes promote sustainable development. The sponsors, developers, and users should be able to maintain a constant pace indefinitely.
Agile ≠ Fast
Agile = Consistent velocity in the face of change
SOLID & TRUE
- SOLID
- Robert C. Martin (Uncle Bob)
- Michael Feathers
- Design Principles and Design Patterns (2000)
- TRUE
- Sandi Metz
- Practical Object-Oriented Design
SOLID Principles
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
Liskov Substitution Principle
Starting with "L"
...because I think it's pretty boring
Liskov Subsitution
Inheritance is not a prerequisite for Object-oriented design
Behavioral Subtyping
Single Responsibility Principle
Actually pretty interesting
Common Sense
Classes & methods should be focused on one thing
?
Same level of abstraction
public async Task<byte[]> DownloadFile(){
var invoice = await _mediator.Send(new InvoiceQuery(carrier));
var orderData = await _mediator.Send(new CustomerData(invoice.Id));
var transformedInvoice = _invoiceService.Manipulate(invoiceData, orderData);
var file = _invoiceService.CreateCsv(transformedInvoice);
return file;
}
Open/Closed Principle
Open for Extension & Closed for Modification
How do we introduce new behavior without modifying existing code?
Design to an interface
Interface Segregation Principle
Clients should not be forced to depend upon interfaces that they do not use.
Dependency Inversion Principle
Methods vs Functions
Not just syntactic differences
Mathematically, a function is defined as "A special relationship where each input has a single output"
Functional Code = Testable Code
Testable code allows us to write tests
Tests allow us to be confident in our code integrity in the face of change
Confidence in our code integrity in the face of change enables us to Develop with Agility
How do we make code more Functional?
Pass in dependencies!
New is Glue - Steve Smith 2011
Quick Demo
Dependency Injection
"After having written nice, decoupled code throughout your code base, the Composition Root is where you finally couple everything, from data access to (user) interfaces." - Mark Seemann Composition Root Reuse
Unix Philosophy
Doug McIlroy (Bell System Technical Journal 1978)
- Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new "features".
- Expect the output of every program to become the input to another, as yet unknown, program. Don't clutter output with extraneous information. ... Don't insist on interactive input.
And a couple other principles that I don't think apply to developing with agility... https://en.wikipedia.org/wiki/Unix_philosophy
TRUE Principles
Transparent
The consequences of change should be obvious in the code that is changing and in distant code that relies upon it.
Related to Single Responsibility
Our methods should be able to stand on their own and be obvious.
Shared Mutable State is the Root of All Evil - Henrik Eichenhardt
Don't use mutable global variables
Reasonable
The cost of any change should be proportional to the benefits the change achieves.
Usable
Existing code should be usable in new and unexpected contexts.
Exemplary
The code itself should encourage those who change it to perpetuate these qualities.
Simple vs Easy
Simple Made Easy - Rich Hickey Strange Loop 2011
Domain Driven Design & Distributed Systems
Loops & Conditionals
Basic control structure
Bread & Butter
Should be avoided
Declarative vs Imperative
Imperative
Declarative
const numbers = [0,1,2,3,4,5,6,7,8,9,10]
const isEven = input => input % 2 === 0
const square = input => input * input
const getSum = (input1, input2) =>
input1 + input2
let squaredEvens = [];
numbers.forEach(x => {
if (isEven(x)) {
squaredEvens.push(square(x))
}
})
let sumSquaredEvens = 0
squaredEvens.forEach(x =>
sumSquaredEvens = getSum(sumSquaredEvens, x))
const numbers = [0,1,2,3,4,5,6,7,8,9,10]
const isEven = input => input % 2 === 0
const square = input => input * input
const getSum = (input1, input2) =>
input1 + input2
const sumSquaredEvens = numbers
.filter(isEven)
.map(square)
.reduce(getSum, 0)
Imperative
const otherFunc = (input1: string, input2: string) => input1 + " " + input1
const yeetFunc = input => "yeet! " + input
const coolFunc = (input1: string, input2: string) => {
let str = ""
if (input == "case1") {
str = otherFunc(input1, input2)
} else if (input === "case2") {
str = otherFunc(input2, input1)
} else {
str = yeetFunc(input1)
}
console.log(str)
}
Declarative
const coolFunc = (input1: string, input2: string) => {
const behavior = behaviorFactory.create(input1, input2)
const str = behavior.execute()
console.log(str)
}
class OtherBehavior implements IBehavior {
input1: string
input2: string
constructor(input1, input2) {
this.input1 = input1
this.input2 = input2
}
execute = () => input1 + " " + input2
}
class YeetBehavior implements IBehavior {
input: string
constructor(input) {
this.input = input
}
execute = () => "yeet! " + input
}
class BehaviorFactory {
static create(input1, input2): IBehavior {
if (input1 == "case1") {
return new OtherBehavior(input1, input2)
} else if (input1 == "case2") {
return new OtherBehavior(input2, input1)
} else {
return new YeetBehavior(input1)
}
}
}
interface IBehavior {
execute(): string
}
Kernighan's lever
Brian Kernighan - The Elements of Programming Style
"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?"
Ben Hunt 2018
Premature Optimization
If you're concerned with performance but not measuring it, you're just guessing
Is the speed worth the cost?
Don't Invent Problems
Use someone else's solution if it's appropriate
Dapper, mediator, serialization tools, standard http verbs & response codes, logging, etc
Focus on Solving the Business Problem
Conclusion
- Use SOLID & TRUE code
- Focus on simple architecture--not necessarily easy architecture
- Prefer declarative code over imperative code
- Don't be clever
- Solve business problems
Developoing with Agility
By joeybrown
Developoing with Agility
- 88