Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Is Your Code Too SOLID? (with transcript)

Is Your Code Too SOLID? (with transcript)

SOLID is a collection of five common object-oriented development principles collected into one handy acronym. Smart people recommend learning about it, and even the name sounds fantastic — who doesn’t want to write “solid” code? Clearly, you should strive to follow all five principles, all the time, in all of your code. Right? Well. Good object-oriented design is indeed helpful, but SOLID as a set of guiding principles really isn’t. Let’s break SOLID down and reveal the limitations of our current approach, and try to understand just how SOLID our code needs to be.

This version includes a transcript. The version WITHOUT a transcript is here:
https://speakerdeck.com/sarahmei/is-your-code-too-solid

sarahmei

July 23, 2015
Tweet

More Decks by sarahmei

Other Decks in Technology

Transcript

  1. S A R A H
    M E I
    W D C N Z
    2 0 1 5
    I S Y O U R C O D E TO O
    S O L I D ?
    https://www.flickr.com/photos/pie4dan/4567311801
    Hello! I gave this talk in July 2015 at WDCNZ in Wellington, New Zealand.

    View full-size slide

  2. DevMynd
    S A R A H M E I
    C H I E F C O N S U LTA N T
    D E V M Y N D
    U N I T E D S TAT E S
    C H I E F O F BA L D E A G L E S
    A N D F R E E D O M

    http://world-map1.org/map1520769_0_0.htm
    http://www.coolholidaygraphics.com/flagday/glittergraphics/flagdayglitter2.gif
    https://www.devmynd.com/images/team/sarah-mei-cutout.png
    I’m Sarah Mei. This is my dorky work picture, and I’m the Chief Consultant at DevMynd.

    I’m from the United States, specifically San Francisco. The US, as you know, is the chief of
    bald eagles and freedom. I copied that directly from the state department website…

    View full-size slide

  3. DevMynd
    I’m a developer, and I work with a lot of Ruby and JavaScript.

    View full-size slide

  4. C O D E BA S E S
    I WO R K O N
    N O R M A L C O D E BA S E S
    https://www.flickr.com/photos/pmarkham/5361277692
    I’m usually working with teams managing large codebases that have become unwieldy and
    hard to change. This is a common problem, right now, among developers of all persuasions.
    In addition to the Ruby and JavaScript folks, I’ve heard it from folks who do .NET, python,
    CSS, Java, and PHP.

    This isn’t a new problem in the industry. IBM was having this problem in the 70s. It’s
    interesting that it’s again at forefront of our collective consciousness right now, though.

    View full-size slide

  5. AT
    F I R S T
    https://www.flickr.com/photos/jakerust/16661140289/
    One theory: code is easier to write than it used to be. There was the explosion of dynamic
    languages in the past 10 years, but also new frameworks in compiled languages, and the
    addition of usable functional languages. All of this has made it easier than ever to generate
    code in volume. You can achieve a large, unwieldy codebase faster than ever before!

    Perhaps even when you’re still a small company. You don’t need to be the size of IBM,
    anymore, to have their codebase problems.

    View full-size slide

  6. https://www.flickr.com/photos/123638374@N08/14184697490
    These codebases are messy, but it doesn’t mean the teams failed at planning. Many teams
    need, at first, the type of very fast iteration that something like Rails brings. Companies that
    planned ahead for a large codebase, and put structures in place, couldn’t iterate as quickly.
    Some of them don’t exist anymore as a result.

    For a business, a messy codebase that works is better than a well-structured one that
    doesn’t. So we should all be so lucky as to have these problems.

    View full-size slide

  7. https://www.flickr.com/photos/123638374@N08/14184697490
    But we need to do something about it! We need to make working in these codebases suck
    less, because this type of thing is what the vast majority of developers work on. We need to
    make working on this code easy and fun again.

    Some people attempt that maneuver this with microservices. That rant is another talk entirely.

    View full-size slide

  8. DevMynd
    https://twitter.com/tenderlove/status/573907559871971328
    Most companies who do microservices end up like this.

    Carving services out of a monolith and carving objects out of a large class are the same skill.
    If you haven’t been doing good object design in your main codebase, I guarantee you’re not
    going to design a good set of services.

    The skill is much easier to practice in a single codebase, where, if you’re wrong, you just
    have adjust object boundaries. If you have to adjust service boundaries when you’re wrong,
    the learning is much more expensive.

    View full-size slide

  9. DevMynd
    https://twitter.com/tenderlove/status/573907559871971328
    So before making microservices, I recommend teams get better at object-oriented design.
    It’s perhaps not as shiny as microservices, but from a cost perspective, it’s more responsible.

    Not coincidentally, we’ve been seeing a resurgence of interest in object-oriented design in
    communities that weren’t that interested a few years ago - particularly the dynamic language
    communities. There are now lots of books, and blog posts, and conference talks (including
    this one) about software design.

    View full-size slide

  10. DevMynd
    S A N D I M E T Z
    E X P E RT AT
    E V E RY T H I N G S E R I O U S LY, G E T I T
    A M A Z I N G B O O K
    http://www.sandimetz.com/ http://www.poodr.com/
    This is the best of the modern takes on object design. Sandi Metz wrote this fantastic book
    on understanding objects. I highly recommend you read it, even if you’re not a Ruby
    developer.

    View full-size slide

  11. DevMynd
    S A N D I M E T Z
    E X P E RT AT
    E V E RY T H I N G S E R I O U S LY, G E T I T
    A M A Z I N G B O O K
    http://www.sandimetz.com/ http://www.poodr.com/
    Now, I love this book, and all the blog posts, and all the conference talks. But what I do in my
    client work doesn’t really look anything like in there. The book uses example code being
    written from scratch to show you how to put the right boundaries around your objects.

    At first, going into large, monolithic, messy codebases, where no one heeded Sandi’s advice,
    and moving things around didn’t seem like ‘object-oriented design’ to me.

    For a long time I called it ‘refactoring.’ It turns out, though, that those are not separate ideas.

    View full-size slide

  12. D E S I G N
    D E C I D I N G
    H OW TO
    A R R A N G E C O D E
    https://www.flickr.com/photos/matley0/3616669592
    Before we go any further, I want to define some terms. Let’s start with software design. I’m
    not talking about architecture, or systems, just within a single codebase - software design is
    really nothing more (or less) than deciding how code is arranged.

    View full-size slide

  13. D E S I G N
    D E C I D I N G
    H OW TO
    A R R A N G E C O D E
    https://www.flickr.com/photos/matley0/3616669592
    Many people think that software design is something completely separate from
    programming. But in reality, when you’re programming, even if you’re not consciously making
    any decisions, you’re still doing design. Every time you put a function in this object and not
    that one, you’re doing design. You can try to do it ahead of time, and some people do, but
    the vast majority of software design done by developers in our industry is inline.

    Just like programming, you’ll be bad at it at first, but just like programming, you get better at
    software design the more you practice.

    View full-size slide

  14. O B J E C T- O R I E N T E D
    D E S I G N
    G R O U P I N G R E L AT E D
    F U N C T I O N A L I T Y I N O B J E C T S
    https://www.flickr.com/photos/mwanasimba/2901201955/
    Now let’s be more specific and define object-oriented design. OOD is deciding how code
    will be arranged, grouping related functionality in objects.

    This does not sound anything like the wikipedia definition of OOD. It’s pretty abstract.
    Perhaps it will help us to talk about what object-oriented design is NOT.

    View full-size slide

  15. DevMynd
    A L A N G UA G E F E AT U R E
    A WAY O F T H I N K I N G
    O B J E C T- O R I E N T E D D E S I G N I S

    https://www.flickr.com/photos/56627607@N05/15635620289/
    1. OOD is not a language feature. It’s a way of thinking.
    You can write object-oriented code in CSS. You can write it in C, or JavaScript, or Java, or
    C#, or Ruby. Languages with explicit syntax support for objects are what you’ll hear people
    call “object-oriented languages.” That just means “languages in which it is more convenient
    to make objects”, but that’s too long for a wikipedia page title, so…

    Object-oriented design is a way of thinking about code arrangement. In some languages it’s
    easier to express than others, but it’s possible anywhere.

    Buy me a beer and ask me about object design in Haskell some time.

    View full-size slide

  16. DevMynd
    A L A N G UA G E F E AT U R E
    A WAY O F T H I N K I N G
    O B J E C T- O R I E N T E D D E S I G N I S
    A D E S T I N AT I O N
    A M E A N S TO A N E N D



    https://www.flickr.com/photos/56627607@N05/15635620289/
    2. Object-oriented code is not a destination. It’s a means to an end.
    You don’t write object-oriented code for its own sake, or because it’s somehow morally or
    professionally “better.” No particular way of arranging code is inherently better than any other
    way. Object-oriented design is a means that we use, mindfully, to move us toward some
    goal.

    And for most of us, that goal is ease of change.

    View full-size slide

  17. https://www.flickr.com/photos/redvers/532076662/
    We build software for people who don’t really know what they want. They imagine one thing
    but they change their mind when they actually see it in action. Or the business shifts focus.
    Or a key person is replaced. Or it’s Tuesday - the only constant in software development is
    that the end goal shifts as we build it. And it wouldn’t do us any good to wait, because the
    act of building is what causes it to shift.

    In theory, object-oriented design makes it easier to respond to shifting requirements.

    View full-size slide

  18. https://www.flickr.com/photos/redvers/532076662/
    But I can tell you that. And Sandi can tell you that. But I’ll bet you most people in this room
    have been on a project where the code was parceled out into objects, and that made it
    harder to respond to shifting requirements, harder to change, rather than easier.

    To figure out how that happens, we have to take a step back and consider our goals when
    we’re designing software.

    View full-size slide

  19. H I G H
    L OW
    H I G H
    L OW
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    There are two useful axes to consider when we’re looking at different ways to design
    software.

    On the bottom, we have the cost of understanding the code (low or high). This is how hard
    is it to figure out what’s going on. On the side, we have cost of changing the code (low or
    high).

    Every choice about how you arrange code - every choice about software design - goes in
    one of these quadrants.

    View full-size slide

  20. H I G H
    L OW
    H I G H
    L OW
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    Let’s start with writing long procedures. Someone does a GET on /calendar to see their
    calendar for the month, and a procedure is executed - a list of instructions. It determines the
    date range, fetches events within that range from the database, draws the right shaped grid,
    places the dates on them and returns that page to the user.

    View full-size slide

  21. H I G H
    L OW
    H I G H
    L OW
    Procedures
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    It’s pretty easy to understand what happens in a procedure. Cost of understanding is low.
    Everything that happens is right there in an ordered list.

    The tradeoff, in a project with lots of long procedures, is that the cost of change is high. So
    procedures belong in the upper left.

    The biggest devil there is duplication, which forces you to change multiple places in the code
    to make a single logical change. You’ll know you have this problem if you end up touching
    every file in the project to make a change that seems like it should have been simple.

    View full-size slide

  22. H I G H
    L OW
    H I G H
    L OW
    Procedures
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    Some projects, having been burned by the high cost of change with procedures, go
    completely in the other direction.

    The system is made of innumerable tiny objects that each don’t do much. When you do that
    GET on /calendar, a RouteReceiver picks up the call…

    View full-size slide

  23. CalendarParamMgr
    GetCalendarIndex
    CalendarRouteResolver
    CalendarRouteResolverFactory
    RouteResolver
    RouteReceiver
    The RouteReceiver creates a RouteResolver that looks at the URL and figures out that it
    needs a CalendarRouteResolver, which it gets from a CalendarRouteResolverFactory, and
    the CalendarRouteResolver looks at what you’re requesting and instantiates a
    GetCalendarIndex object, which sends your params to a CalendarParamManager…and so
    on.

    The sequence of events isn’t written down anywhere in the code. You just have to trace it
    through to figure it out. Little pieces of functionality are spread across many classes.

    View full-size slide

  24. H I G H
    L OW
    H I G H
    L OW
    Procedures
    Set of Small Objects
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    A system like this is harder to understand than a procedure. The cost of understanding is
    high. A list of instructions will always be easier to understand than a set of objects.

    However, once you understand the system, the cost of change is low. Assuming you’ve got
    the right abstractions (a notion we’ll deal with in a moment), it’s relatively easy to take, for
    example, one params handler out and start using another. So a set of small objects goes
    down in the lower right.

    View full-size slide

  25. H I G H
    L OW
    H I G H
    L OW
    Procedures
    ⚠⚡
    Set of Small Objects
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    Now let’s talk about this quadrant here. Danger zone! Code with a high cost of
    understanding AND a high cost of change is the worst of both worlds.

    There are two types of codebases here, both of which are distressingly common.

    View full-size slide

  26. H I G H
    L OW
    H I G H
    L OW
    Procedures
    Set of Small Objects
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    Really big objects

    The first is a codebase made up of really big objects. Perhaps the framework dictated an
    initial set of classes, and all behavior just sort of accreted onto them.

    The huge objects always seem to be the ones that are core to the application. They change
    with almost every commit, and changes go wrong easily, because all that functionality in one
    place means unintentional interference is almost a given.

    View full-size slide

  27. H I G H
    L OW
    H I G H
    L OW
    Procedures
    Set of Small Objects
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    Really big objects

    The wrong set
    of small objects

    The other type of codebase is small objects gone wrong, which is what happens when you
    try to break down a big class but don’t get the object boundaries quite right.

    Then it’s both hard to understand, because it’s objects, and hard to change, because for
    one logical change you still have to make changes in lots of different places.

    The wrong set of small objects is the worst-case scenario. Really big objects are bad, but not
    that bad.

    View full-size slide

  28. H I G H
    L OW
    H I G H
    Procedures

    Set of Small Objects
    L OW
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    Really big objects

    The wrong set
    of small objects

    So there are two questions here. The first: how can we get to the lower left? Can we? Is that
    the perfect solution that cannot exist?

    The second: how do we move our big lumbering codebases out of the upper right? It doesn’t
    really matter what direction we move; anywhere will be an improvement. But usually, when a
    codebase is this size, reducing the cost of change is worth increasing the cost of
    comprehension. So most people want to move down into sets of small objects.

    View full-size slide

  29. H I G H
    L OW
    H I G H
    Procedures

    Set of Small Objects
    L OW
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    Really big objects

    The wrong set
    of small objects

    This is where they start reading about things like SOLID and design patterns, hoping they
    can figure out how to make the move. Let’s talk about patterns briefly before we dive into
    SOLID.

    View full-size slide

  30. PAT T E R N S
    https://www.flickr.com/photos/dakluza/6665460621
    A pattern is an example set of object boundaries that seems to work well in many situations.

    I’ll give you an example of a pattern called the Observer.

    View full-size slide

  31. O B S E R V E R
    N E W E M A I L
    User

    Let’s say you have a User class, and when a new user is created, meaning someone has
    signed up for your service, it automatically sends them an email to get them to confirm their
    account. Normally this is fine. If your user class is small and your object graph is
    uncomplicated, then it’s fine to leave this here.

    But a lot of times, the user gets to be one of the biggest classes in the system, and it can be
    annoying to have it send email every time you make one. You have to find ways to turn it off
    when you’re creating a user in your tests, and every test requires you to create a user - so to
    make it easier to turn off, you want to separate user creation from email sending.

    View full-size slide

  32. O B S E R V E R
    User UserObserver


    You create a new class UserObserver, and you move the email functionality over there.
    UserObserver gets a creepy set of googly eyes so it can observe the User class.

    View full-size slide

  33. O B S E R V E R
    User UserObserver


    ! E M A I L
    N E W
    Now when a new user is created, the UserObserver notices, and sends email. That’s cool,
    right? You’ve reduced the size of your User class and made it easier to turn off email
    sending. You’ve made the code easier to change. But you’ve also made it harder to
    understand.

    You used to only have to look one place to see everything that happened when a user was
    created. It all happened in the User class. Now you have two places to look. Because it’s in a
    separate class, other people may not know it exists, let alone that they have to turn it off, and
    then be unpleasantly surprised when the users they create get emails.

    View full-size slide

  34. H I G H
    L OW
    H I G H

    L OW

    E A S I E R TO
    C H A N G E
    H A R D E R TO
    U N D E R S TA N D
    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E
    If you’re starting out with a codebase in the upper right quadrant and you apply the observer
    pattern as we just talked about, you move it down here. (That’s the confounded face emoji, in
    case you were wondering.) You’ve made it easier to change but harder to understand.

    Applying a pattern improves changeability but worsens understandability.

    View full-size slide

  35. https://www.flickr.com/photos/betsyweber/5902421276
    Patterns aren’t free. Many people read about patterns and then start looking for
    opportunities to apply them. They assume that making less-structured code into patterns is
    always a good idea. They don’t realize that everything has a cost.

    And determining whether it’s worth it is the hard part. At what point does lower cost of
    change outweigh higher cost of understanding? There’s no single answer. It’ll be different in
    different parts of your code, and it will be different at different times in the same part of the
    code.

    View full-size slide

  36. https://www.flickr.com/photos/grungetextures/4162992874
    So while patterns give us ideas about how to break up code, it gives us no guidance on
    when.

    Let’s look at the principles and see if they help with that.

    View full-size slide

  37. S O L I D
    https://www.flickr.com/photos/chrisjfry/309574260/
    SOLID. It sounds so awesome! Who doesn’t want “solid” code? Or to be a “solid”
    programmer? There are many object-oriented principles. Academia has been studying object
    orientation for decades. However, academics tend not to deal in volumes of code, so most of
    the principles are highly academic.

    In the 90s, Robert Martin took the five principles that seemed the most relevant to working
    software developers, and assembled this acronym. Let’s talk about what each letter means.

    View full-size slide

  38. N A M E S U M M A RY U T I L I T Y
    S
    O
    L
    I
    D
    We’ll be filling in this chart as we go. We have three columns:

    1. The name of the principle

    2. A summary of what it means (not what it says)

    3. A measure of how useful it will be in our everyday developer life.

    The first letter of SOLID is S!

    View full-size slide

  39. S I N G L E R E S P O N S I B I L I T Y
    P R I N C I P L E
    S O L I D
    https://www.flickr.com/photos/shenamt/8582808329/
    S is for the single responsibility principle. It says that a class should have one
    responsibility, or to put it another way, one reason to change. This was first articulated by
    Rebecca Wirfs-Brock in the 80s.

    It’s a fancy way of saying that smaller things are easier to understand, and harder to mess
    up, than larger ones.

    View full-size slide

  40. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O
    L
    I
    D
    As far as utility goes, it’s sort in the middle. The difficulty hinges on the definition of
    “responsibility.” If you’ve got a class that finds users, persists users, validates users, allows
    access to its related objects, and contains business logic related to users, you could
    plausibly say it’s got one responsibility: it manages the user.

    View full-size slide

  41. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O
    L
    I
    D
    You could equally plausibly say that all of those things are separate responsibilities that all
    belong in different classes. The principle doesn’t give you guidance because there is no
    universal right answer. Sometimes it makes sense to put all that together; other times it
    doesn’t. And that shifts over time, even for the same codebase.

    The answer to every question in software development is “it depends.”

    View full-size slide

  42. O P E N / C L O S E D
    P R I N C I P L E
    S O L I D
    https://www.flickr.com/photos/28481088@N00/4063800774/
    O is for the open/closed principle. This is usually stated as “a class should be open to
    extension but closed to modification.” Bertram Meyer came up with this in the 80s.

    It’s a fancy way of saying that editing existing code is more difficult and more error-prone
    than just adding new code, so: arrange your codebase such that we can add new
    functionality just by writing new code.

    View full-size slide

  43. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O O P E N / C L O S E D
    D O N ’ T E D I T C O D E ;
    A D D N E W C O D E
    L O W
    L
    I
    D
    As far as utility goes…it sounds great, doesn’t it? But it’s hard to conceive of how it could
    ever happen in a codebase of significant size. It’s not super practical day-to-day.

    View full-size slide

  44. L I S KOV S U B S T I T U T I O N
    P R I N C I P L E
    S O L I D
    https://www.flickr.com/photos/vpickering/14416940341/
    L is for the Liskov substitution principle. This is perhaps the most academic of the SOLID
    principles. It is a precise, mathematical statement.

    Here it is:

    View full-size slide

  45. DevMynd
    Let Φ(x) be a property
    provable about objects x of
    type T. Then Φ(y) should be
    true for objects y of type S
    where S is a subtype of T.
    Let theta(X) be a property provable about objects X of type T. Then theta(Y) should be true for
    objects Y of type S where S is a subtype of T.

    …ok…

    View full-size slide

  46. L I S KOV S U B S T I T U T I O N
    P R I N C I P L E
    S O L I D
    https://www.flickr.com/photos/vpickering/14416940341/
    It’s a fancy way of saying: anywhere you can use an instance of a class Foo, you should be
    able to use an instance of class Bar that subclasses Foo. And nothing should go wrong.

    The Liskov Substitution Principle was formulated by Barbara Liskov in 1987, when she was
    in her late 40s.

    View full-size slide

  47. I T ’ S N E V E R
    TO O L AT E TO
    https://www.flickr.com/photos/wcouch/8578612563/
    It’s never too late to be amazing.

    View full-size slide

  48. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O O P E N / C L O S E D
    D O N ’ T E D I T C O D E ;
    A D D N E W C O D E
    L O W
    L L I S K O V
    S U B S T I T U T I O N
    I N H E R I TA N C E . I T ’ S A
    T H I N G .
    L O W
    I
    D
    The Liskov substitution principle basically says “inheritance…it’s a thing.” It seems pretty
    obvious to us at this point, but when it was first articulated, it was not at all so obvious.

    The LSP was such a good idea that, since it was introduced, we have baked it into our
    languages. It’s now part of the air we breathe, and we don’t even notice it’s there.

    It’s had a huge impact on the way we write software, which, I would guess, is why Martin
    chose to include it in SOLID, despite its academic nature. However, while it is fundamental,
    it’s not really a great source of practical help day-to-day. Utility is low.

    View full-size slide

  49. I N T E R FA C E
    S E G R E G AT I O N
    P R I N C I P L E
    S O L I D
    https://www.flickr.com/photos/66126733@N04/8477516123/i
    I stands for the interface segregation principle, which says that classes should only have to
    depend on the part of an interface they actually need.

    It’s a fancy way of saying “don’t let changes to unrelated functionality in a class affect other
    things that use it.”

    View full-size slide

  50. W H Y S E G R E G AT E I N T E R FA C E S ?
    Musician
    AlbumCreator
    recordSong
    editSong
    mixSong
    sellMerch
    playSetList Gig
    Let’s say you have a class Musician that has five methods on it: recordAlbum, editSong,
    mixSong, playSetList, and sellMerch.

    It has two classes that consume it: AlbumCreator, and Gig. They use different sets of
    methods and are not related to each other.

    View full-size slide

  51. W H Y S E G R E G AT E I N T E R FA C E S ?
    Musician
    AlbumCreator
    recordSong
    editSong
    mixSong
    sellMerch
    playSetList Gig
    driveVan



    Then you add another method - driveVan - that is only used by Gig. You’d expect at this
    point that Musician and Gig would have to be recompiled, but it turns out all consumers must
    be recompiled. Even AlbumCreator, which didn’t change at all.

    With just two consumers - who cares? But what if Musician had hundreds of consumers that
    had to be recompiled every time you make any change? You can see how it’s a huge pain. It
    makes re-running a test, for one thing, a very long process.

    View full-size slide

  52. W H Y S E G R E G AT E I N T E R FA C E S ?
    StudioActions
    AlbumCreator
    recordSong
    editSong
    mixSong
    sellMerch
    playSetList
    Gig
    driveVan
    VenueActions
    Interface segregation says you should break Musician into two different classes -
    StudioActions and VenueActions. That way, when a new method is added to VenueActions,
    AlbumCreator won’t need recompiling.

    View full-size slide

  53. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O O P E N / C L O S E D
    D O N ’ T E D I T C O D E ;
    A D D N E W C O D E
    L O W
    L L I S K O V
    S U B S T I T U T I O N
    I N H E R I TA N C E . I T ’ S A
    T H I N G .
    L O W
    I I N T E R FA C E
    S E G R E G AT I O N
    M A K E T H E A P I
    S M A L L
    H I G H
    D
    Anyway - it makes more practical sense in compiled languages, so many of us who primarily
    use dynamic languages dismiss it as unrelated.

    However, it does have a really really useful core idea: if different consumers of a class use
    non-overlapping sets of methods, that’s a sign that that class has multiple responsibilities
    (see S). So I put its utility high, relative to the other principles we’ve looked at here.

    View full-size slide

  54. D E P E N D E N C Y I N V E R S I O N
    P R I N C I P L E
    S O L I D
    https://www.flickr.com/photos/hansmaulwurf/13702053625/
    And finally, D, which stands for the dependency inversion principle. Not dependency
    injection, which is one particular implementation of this idea.

    Dependency inversion says “depend on abstractions rather than concretions.”

    This is a fancy way of saying: if you want to make a new thing, a new instance of a class,
    inside another class, think about creating it outside and just passing it in to the constructor,
    instead. This has the effect of moving all your choices towards the edges of your system.

    View full-size slide

  55. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O O P E N / C L O S E D
    D O N ’ T E D I T C O D E ;
    A D D N E W C O D E
    L O W
    L L I S K O V
    S U B S T I T U T I O N
    I N H E R I TA N C E . I T ’ S A
    T H I N G .
    L O W
    I I N T E R FA C E
    S E G R E G AT I O N
    M A K E T H E A P I
    S M A L L
    H I G H
    D D E P E N D E N C Y
    I N V E R S I O N
    M O V E C H O I C E S T O
    E D G E S O F S Y S T E M
    H I G H
    There are lots of “dependency injection” frameworks. Angular, .NET, Java. They make it
    easier to test classes. Given that most of them have essentially become gigantic global state,
    though, it’s sometimes a bit difficult to tie implementations back to the principle. But
    dependency inversion does seem to be, theoretically, something a lot of people see. Utility
    high.

    View full-size slide

  56. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O O P E N / C L O S E D
    D O N ’ T E D I T C O D E ;
    A D D N E W C O D E
    L O W
    L L I S K O V
    S U B S T I T U T I O N
    I N H E R I TA N C E . I T ’ S A
    T H I N G .
    L O W
    I I N T E R FA C E
    S E G R E G AT I O N
    M A K E T H E A P I
    S M A L L
    H I G H
    D D E P E N D E N C Y
    I N V E R S I O N
    M O V E C H O I C E S T O
    E D G E S O F S Y S T E M
    H I G H
    Now that we’ve filled it all in, let’s look at this chart for a moment.

    SOLID has principles of vastly varying degrees of utility, or concreteness. Utility, or
    immediately applicability to code you’re writing today, is one end of a spectrum. The
    opposite end is abstraction, which describes a general rule that sounds like a good idea, but
    is hard to connect to code you’re looking at in your editor.

    View full-size slide

  57. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O O P E N / C L O S E D
    D O N ’ T E D I T C O D E ;
    A D D N E W C O D E
    L O W
    L L I S K O V
    S U B S T I T U T I O N
    I N H E R I TA N C E . I T ’ S A
    T H I N G .
    L O W
    I I N T E R FA C E
    S E G R E G AT I O N
    M A K E T H E A P I
    S M A L L
    H I G H
    D D E P E N D E N C Y
    I N V E R S I O N
    M O V E C H O I C E S T O
    E D G E S O F S Y S T E M
    H I G H
    O and L are the most abstract, I and D are the most concrete, and S sits in the middle. We’ve
    got at least three different levels of abstraction here. None of them, even I or D, seems like
    it’s actually useful day-to-day for refactoring code. It’s nice to say make the api on an object
    small, but … that ship has sailed.

    We need things that are more concrete to guide our everyday programming. But it’s not clear
    how to find them.

    View full-size slide

  58. https://www.flickr.com/photos/29295318@N08/19322005453
    So the principles are too abstract. The patterns, as we talked about earlier, are more
    concrete, but still don’t help us with when. We need something else - our last missing puzzle
    piece - to help us actually move our code forward.

    Here’s an idea that may be useful.

    View full-size slide

  59. S T R AT E G Y &
    TA C T I C S
    https://www.flickr.com/photos/teegardin/6150427712/
    “Strategy” and “tactics” are military concepts. I didn’t grow up in a military family, so for most
    of my life, I thought of these two words as essentially synonymous. But they’re not.

    A strategy is a high-level objective that will move us closer to some goal.

    A tactic is something you do on the ground to achieve that strategy.

    Here’s an example of how these are different.

    View full-size slide

  60. \ O /
    \ O /
    \ O /
    G OA L : P R O M O U N TA I N E E R
    S T R AT E G Y: C L I M B T H I S M T N
    TA C T I C S : T H E R O U T E U P
    https://www.flickr.com/photos/frenchy/222016256/
    Your goal: be a pro mountaineer. Your strategy: climb this mountain that few people have
    climbed before. You’re starting from the bottom (lower left) and need to make it to the
    summit (middle top).

    There are no trails - you need to figure out your own path up. The strategy is where you want
    to be at the end of the day, and your tactics are how you get there. Tactics includes the
    planned route, and contingency plans, and are guided by (and may be changed by) the
    strategy as you walk up the mountain.

    View full-size slide

  61. \ O /
    \ O /
    \ O /
    G OA L : P R O M O U N TA I N E E R
    S T R AT E G Y: C L I M B T H I S M T N
    TA C T I C S : T H E R O U T E U P
    1
    2
    3
    https://www.flickr.com/photos/frenchy/222016256/
    Possible routes include: 1) walking along the treeline and then going up the right-hand ridge.
    2) climbing this rockface, sliding along the little shelf there, and then along the left-hand ridge
    to the top. 3) just going for it, straight up the face. They all involve different tactics - walking
    vs. climbing vs. rappelling, etc.

    The actual route you take will depend on the weather, your skills, your gear, and many other
    things.

    View full-size slide

  62. G OA L : P R O M O U N TA I N E E R
    S T R AT E G Y: C L I M B T H I S M T N
    TA C T I C S : T H E R O U T E U P
    \ O /
    https://www.flickr.com/photos/frenchy/222016256/
    Once you choose a route, it’s still probably not what’s actually going to happen. If you get to
    the base of the ridge and discover that an avalanche has made it too perilous to go up that
    way, you change tactics, because your strategy of climbing the mountain is no longer in line
    with your original tactics. Part of your tactics includes determining when to change tactics.

    View full-size slide

  63. G OA L : P R O M O U N TA I N E E R
    S T R AT E G Y: C L I M B T H I S M T N
    https://www.flickr.com/photos/frenchy/222016256/
    What would happen if all you had was a strategy? All you know is “ok, there’s that mountain,
    I need to get to the top of it.” If you set out to achieve this strategy without working out any
    of little steps you can take to get there…it’s not likely to work out for you very well. You may
    get to the top accidentally, but it’s more likely that you’ll try a few fruitless paths, find yourself
    in a valley you can’t get out of, and then have to signal to the park rangers to airlift you out.

    You need to have tactics in mind, or you probably won’t achieve your strategy.

    View full-size slide

  64. G OA L : C H A N G E A B L E S O F T WA R E
    S T R AT E G Y:
    TA C T I C S :
    O O D E S I G N
    S O L I D
    https://www.flickr.com/photos/sunfox/5084875405/
    Our actual goal is changeable software, rather being a pro mountaineer. Changeable
    software is the promise of objects.

    One way to think about strategies & tactics for changeable software is that our strategy is
    object oriented design. Our tactics would then be things like SOLID & patterns. This is how
    object-oriented design is taught, particularly in academia, and it is how most developers look
    at it, whether or not they could articulate it.

    View full-size slide

  65. G OA L : C H A N G E A B L E S O F T WA R E
    S T R AT E G Y:
    TA C T I C S :
    O O D E S I G N
    S O L I D
    https://www.flickr.com/photos/sunfox/5084875405/
    There’s a problem, though, with this picture of the world. “Object-oriented design” isn’t
    actually a strategy. Remember, a strategy describes what we want our world (in this case our
    codebase) to look like at the end of the day.

    “Object-oriented design” doesn’t describe that. On the other hand, SOLID is reasonably
    good at describing what our codebase should look like when it’s “finished.” Hmm.

    View full-size slide

  66. N A M E S U M M A RY U T I L I T Y
    S S I N G L E
    R E S P O N S I B I L I T Y
    O N E R E S P O N S I B I L I T Y
    P E R C L A S S
    M E D
    O O P E N / C L O S E D
    D O N ’ T E D I T C O D E ;
    A D D N E W C O D E
    L O W
    L L I S K O V
    S U B S T I T U T I O N
    I N H E R I TA N C E . I T ’ S A
    T H I N G .
    L O W
    I I N T E R FA C E
    S E G R E G AT I O N
    M A K E T H E A P I
    S M A L L
    H I G H
    D D E P E N D E N C Y
    I N V E R S I O N
    M O V E C H O I C E S T O
    E D G E S O F S Y S T E M
    H I G H
    Single responsibility principle, for example, would make a good strategy. It describes a state
    of our codebase in which there is one responsibility (at the right granularity!) per class. If we
    had that, our codebase would be more changeable.

    Open/closed describes this codebase utopia where you never have to edit code to add
    features.

    In fact all these principles are, at some level, descriptions of when you know your code is
    “right.” “If your code were like this, it would be more changeable.”

    View full-size slide

  67. G OA L : C H A N G E A B L E S O F T WA R E
    S T R AT E G Y:
    TA C T I C S :
    S O L I D P R I N C I P L E S
    https://www.flickr.com/photos/sunfox/5084875405/
    SOLID is a great set of strategies. Our problem is that we’ve been using them like tactics.

    For example, you can’t apply single responsibility principle directly to a thousand-line class.
    A class like that has a muddy set of abstractions spread across multiple methods each that
    are hard to distinguish. When you squint at the class and envision how you’d break it up,
    you’ll most likely be wrong. The abstractions are, by definition, hard to see, because if they
    weren’t, you’d have done something about it already.

    View full-size slide

  68. G OA L : C H A N G E A B L E S O F T WA R E
    S T R AT E G Y:
    TA C T I C S :
    S O L I D P R I N C I P L E S
    https://www.flickr.com/photos/sunfox/5084875405/
    Trying to just eyeball a large class and see what the right abstractions are is like trying to
    head towards the summit without planning a route ahead of time.

    But we do this a lot! I’ve been on many teams where a class got too monstrous, so they’d
    take a week off from doing feature work. They’d spend that week eyeballing the class and
    trying to refactor it into the right abstractions. Teams do these a lot. I call this a “stop-the-
    world” refactoring.

    View full-size slide

  69. G OA L : C H A N G E A B L E S O F T WA R E
    S T R AT E G Y:
    TA C T I C S :
    S O L I D P R I N C I P L E S
    https://www.flickr.com/photos/sunfox/5084875405/
    What’s wrong with a stop-the-world refactoring? Refactoring is part of being a good software
    engineer, right? Taking time to clean up & make code better is important. Right?

    View full-size slide

  70. https://www.flickr.com/photos/jonathancohen/15420956905
    Of course it’s important to refactor code. However.

    Two interesting things happen when you stop the world to refactor:



    1. The product team is unhappy. You’re taking a whole week “off,” from their perspective,
    meaning you’re not working on anything they can see.

    2. Because you’re time-limited, you feel pressure at the end of the week to break that class
    up somehow, even if you’re not entirely sure yet where the right boundaries are.

    #2 is exactly how you end up with one of these.

    View full-size slide

  71. H I G H
    L OW
    H I G H
    L OW
    The wrong set
    of small objects

    C O S T O F U N D E R S TA N D I N G
    C O S T O F C H A N G E

    View full-size slide

  72. DevMynd
    T H E O N LY WAY TO M A K E A S TO P - T H E - WO R L D
    R E FA C TO R I N G WO R K I S TO N OT D O I T .
    http://www.imdb.com/title/tt0086567/
    The only way to make a stop-the-world refactoring work is to not do it.

    It’s like global thermonuclear war. The only winning move is not to play.

    When we apply strategies as though they were tactics, we end up with these ham-handed,
    mostly unplanned, unpopular changes that, more often than not, leave us in a worse place
    than before.

    View full-size slide

  73. https://www.flickr.com/photos/frenchy/222016256/
    When we do one of those stop the world refactorings, we’re doing this. And it’s frustrating.

    Ever wonder why good developers who started out in an OO language are turning to
    functional programming?

    Certainly novelty is part of it, but there’s also a strong undercurrent of fundamental criticism
    of OO. They say it just doesn’t work. And that’s because there’s a summit there, and they can
    see it, and it seems like they should be able to get to it. But they can’t.

    View full-size slide

  74. https://www.flickr.com/photos/frenchy/222016256/
    What we’re missing is a set of tactics that gets us up there in explicit, small steps. We need
    things we can do every day, as we’re doing feature work, to inline the refactoring time and
    allow the right objects emerge from the mess over time.

    Right now we don’t have very many of tactics. One thing we do have, though, is lots of rules
    that tell us what not to do.

    View full-size slide

  75. DevMynd
    post.comments.first.author.email
    NOPE
    For example, we have the Law of Demeter that tells us that `post.comments` is ok, but more
    method calls chained on the end of it gets a big ol’ NOPE. When we make long chains of
    method calls on different objects, we’re introducing coupling we don’t want.

    So, ok, don’t do that.

    View full-size slide

  76. DevMynd
    https://www.flickr.com/photos/83794824@N07/8059223873/
    We also have code smells, which tell us when something might be wrong with our code.

    We have are very few ‘do’s. Most of our rules at this level are ‘don’t’s.

    We need ‘do’s. We need a set of tactics. Let’s make one! I’ve made my own acronym. :D

    View full-size slide

  77. S TA B L E
    STABLE. This mountain is SOLID, which is cool. But when I’m trying to go up it, what I really
    need is stability. I want it to, for example, not be a volcano, not avalanche out from under my
    feet, not drop boulders on me, etc. I need it to be solid, sure. But at a more immediate level,
    when I’m making my way on the ground, I need stability.

    The same is true in my codebase. I want to know that if I make a change in one part of the
    code, it won’t cause an avalanche on the other side of the mountain.

    View full-size slide

  78. S TA B L E
    So let’s go through these tactics.

    I took the six things I wanted to say, and worked pretty hard, actually, to make them into an
    acronym. Hey, if it worked for SOLID…

    View full-size slide

  79. S TA B L E
    S M E L L Y O U R C O D E
    https://www.flickr.com/photos/pedrosimoes7/169983321/
    S: smell your code.
    Study code smells and the other ‘don’t’s, so that you can start identifying things you could
    fix.

    Martin Fowler’s Refactoring book is great for this. I’ve worked with teams who would pick
    one code smell for a lunch & learn each week, and discuss and look for examples in their
    code.

    You want to start noticing & naming the problems in the code you look at, even though you
    won’t be fixing them all yet.

    View full-size slide

  80. T I N Y P R O B L E M S
    F I R S T
    S TA B L E
    https://www.flickr.com/photos/ian-arlett/13706787684/
    T: tiny problems first.
    In messy code, there will be lots of smells. They’ll overlap and intertwine and interfere with
    each other. It will be hard to see what should be made with all this mess.

    The best way to get started is to pick a really small problem that you know how to solve in a
    very concrete way, and just fix that. For example, rename a variable whose usage has
    diverged from its name.

    View full-size slide

  81. S TA B L E
    https://www.flickr.com/photos/ian-arlett/13706787684/
    T I N Y P R O B L E M S
    F I R S T
    Pick the smallest problem to fix even though you see enough of the bigger problems to start
    guessing their answers. Your goal is to see the large problems better, by clearing away
    the small problems obscuring them. The more information you gather about these larger
    problems, the more likely your eventual abstractions will be right.

    Proper abstractions are worth waiting for. Let them emerge from the code you have by
    clearing away the easy cruft.

    View full-size slide

  82. A U G M E N T T E S T S
    S TA B L E
    https://www.flickr.com/photos/horiavarlan/4273968004/
    A: augment your tests.

    You will almost certainly have to do this to be able to refactor large classes.

    You need integration tests at one level higher than the class you’re working on, and you need
    these in place before you start doing any of this. For example, in a server-side MVC
    application, if you want to refactor a big controller method, you’ll need view-level integration
    tests in place that exercise it. If you want to refactor a big model, you’ll need controller-level
    integration tests.

    View full-size slide

  83. https://www.flickr.com/photos/horiavarlan/4273968004/
    S TA B L E
    A U G M E N T T E S T S
    In general: test behavior, not implementation. That’s why you go one level up. You need
    tests that describe the behavior you want to keep. This may mean getting rid of some
    lower-down unit tests, and/or writing a new set of a tests at a level you haven’t had them
    before.

    View full-size slide

  84. S TA B L E
    BA C K U P
    https://www.flickr.com/photos/mjonasson/19228720295/
    B: back up when it’s useful.

    When the code has an abstraction in it that is no longer serving you well, sometimes the
    most useful thing to do is to ‘rewind’ the code into a more procedural state, put all the
    duplication back, and start again. It is much, much easier to move from a procedure to the
    right set of objects, than it is to move from the wrong set to the right set.

    Don’t get caught by the sunk cost fallacy. Don’t forge ahead with a set of objects that don’t
    even fit now, let alone the future.

    View full-size slide

  85. S TA B L E
    L E AV E I T
    B E T T E R T H A N
    Y O U F O U N D I T
    https://www.flickr.com/photos/spierisf/6989462184/
    L: Leave it better than you found it.
    During any one expedition into the code to add a feature or fix a big, you won’t be able to fix
    all the problems you see. (Can’t hug every cat! [Hello memes from 2008.]) Sometimes the
    only thing you have time to do alongside your stated goal is rename a method so it describes
    its behavior better.

    View full-size slide

  86. https://www.flickr.com/photos/spierisf/6989462184/
    S TA B L E
    L E AV E I T
    B E T T E R T H A N
    Y O U F O U N D I T
    That action seems so small, doesn’t it? Our instinct is to save a bunch of those up and do
    them at once. Don’t give into that stop-the-world temptation, though. Fix one thing, the
    smallest thing, while you’re working on a story that concerns the code.

    I was a girl scout, and one of our rules when we were out camping was that we always left a
    camp site in better shape than when we arrived. Over time this made a better experience for
    everyone, including ourselves.

    View full-size slide

  87. https://www.flickr.com/photos/spierisf/6989462184/
    S TA B L E
    L E AV E I T
    B E T T E R T H A N
    Y O U F O U N D I T
    When you change that one method name, you’re removing a little piece of cognitive
    dissonance. The next person to come through this code will be able to understand it a little
    more easily. Maybe the big abstraction will suddenly be obvious. Or maybe they’ll just fix the
    next smallest thing. The key insight is that little things add up over time.

    View full-size slide

  88. E X P E C T
    G O O D
    R E A S O N S
    S TA B L E
    https://www.flickr.com/photos/an_untrained_eye/5012331537
    E: expect good reasons.
    Assume past developers had good reasons to write the code they did. Some code looks so
    horrible that we think, “what idiot wrote this? Why would anyone ever do it this way?” And
    then you run git blame and find out it was you, six months ago. Or it was one of the devs you
    really admire.

    View full-size slide

  89. https://www.flickr.com/photos/an_untrained_eye/5012331537
    S TA B L E
    E X P E C T
    G O O D
    R E A S O N S
    There are forces at work on the code beyond the developer’s experience and technical skill.
    Deadlines, relationships with other groups like product, QA, and operations, company
    financial situation…all these and more leave their fingerprints on the codebase. Start thinking
    about the social pressures that affect your codebase, and many things will make a lot more
    sense.

    View full-size slide

  90. S S M E L L Y O U R C O D E
    T T I N Y P R O B L E M S F I R S T
    A A U G M E N T T E S T S
    B B A C K U P
    L L E AV E I T B E T T E R
    E E X P E C T G O O D R E A S O N S
    These are little things you can do to inline-refactor large classes as you’re making progress
    on features. You don’t have to stop the world. You can rebuild trust with your product team,
    and as a bonus, you’re much more likely to end up with the code you want.

    It’s a cycle. Keep fixing the small problems, and the solutions to the large ones will become
    obvious.

    View full-size slide

  91. S S M E L L Y O U R C O D E
    T T I N Y P R O B L E M S F I R S T
    A A U G M E N T T E S T S
    B B A C K U P
    L L E AV E I T B E T T E R
    E E X P E C T G O O D R E A S O N S
    You might ask, don’t you still have to stop the world sometimes? After all, once you’ve
    cleaned up enough of the small problems so that the solution to the larger problem becomes
    obvious, you do have to still actually solve the larger problem.

    View full-size slide

  92. DevMynd
    https://twitter.com/kentbeck/status/250733358307500032
    Kent Beck has an interesting way of talking about this. “For each desired change, make the
    change easy (warning: this may be hard), and then make the easy change.”

    When you remove small problems, the big problems become obvious, AND they become
    much easier. You amortize the cost spent fixing the big problems and as a result you end up
    with higher-quality solution. And when you see it, will look, in retrospect, so completely #@$
    %ing obvious.

    View full-size slide

  93. DevMynd
    https://twitter.com/kentbeck/status/250733358307500032
    Kent’s warning is important to notice, though. “This may be hard.”

    The STABLE cycle won’t always prevent you from extracting the wrong objects, or trying a fix
    that doesn’t work. You’ll still have go down some paths that lead to dead ends. But in this
    process, that’s expected. Back up & try another. That’s why “back up” is one of the essential
    steps in this process.

    View full-size slide

  94. https://www.flickr.com/photos/frenchy/222016256/
    But this time, we aren’t wandering aimlessly, like we were back here.

    View full-size slide

  95. \ O /
    https://www.flickr.com/photos/frenchy/222016256/
    We’re moving tactically. We’re following steps that we know will get us there. Even if the final
    shape of the journey doesn’t look like what we thought it would when we started.

    View full-size slide

  96. https://www.flickr.com/photos/bevrichardmartin/5985876061/
    https://www.flickr.com/photos/discarnate/27211024/
    https://www.flickr.com/photos/lynardie/16191183291/
    Work on making your code STABLE, and it will eventually be SOLID. You can get to the top.
    You can be the success story. Just make sure you head up with a plan.

    View full-size slide

  97. DevMynd
    Sarah Mei
    [email protected]
    @sarahmei
    THANK YOU!
    Thank you!

    View full-size slide