The Complexity Trap: Think Before You Leap

The Complexity Trap: Think Before You Leap

Recently, many people in the functional programming community, and specifically in the Scala community, seem to follow the trend of solving their programming problems with more and more fancy abstractions and techniques. If in doubt, we throw a monad at the problem, and if that’s not good enough, we’ll make it free. Naturally, to top it off, we have to sprinkle the whole thing with some type-level programming, because this is common courtesy these days.
LAMBDA WORLD 2018, Cádiz

In this talk I want to challenge some of the fundamental assumptions of how we think and work. With all our sophisticated engineering, are we actually solving the right problems? Are we rushing towards technologically exciting solutions too quickly? How much of the complexity in our software is inherent in the problem domain, and how much of it is of our own making? We may have honorable intentions, but do our solutions come at an acceptable price? Maybe it’s time to slow down, think about what the problems we have to solve actually are, and how to do so in the simplest way possible.

7abf07f13ed689874500c08bc7fbd543?s=128

Daniel Westheide

October 26, 2018
Tweet

Transcript

  1. LAMBDA WORLD CADIZ 2018 The Complexity Trap: Think Before You

    Leap Daniel Westheide @kaffeecoder
  2. What is this talk about? Photo by Domenico Gentile on

    Unsplash
  3. Agenda • What is complexity? • The complexity trap &

    how to escape it – Neglecting the costs – Embracing industry standards – Maximum zoom • Conclusions
  4. complex Photo by drmaket lab on Unsplash

  5. simple Photo by Johny Goerend on Unsplash

  6. Types of complexity • Essential complexity • Accidental complexity

  7. Metrics or it didn‘t happen! // Cyclomatic complexity = 1

    def squared(x: Int): Int = x * x // Cyclomatic complexity = 2 def max(x: Int, y: Int): Int = if (x >= y) x else y
  8. Cognitive complexity public static int sumOfPrimes(int max) { int total

    = 0; OUT: for (int i = 1; i <= max; ++i) { for (int j = 2; j < i; ++j) { if (i % j == 0) { continue OUT; } } total += i; } return total; } def sumOfPrimes(max: Int): Int = Stream .range(1, max + 1) .filter(i => (2 until i).forall(j => i % j != 0)) .sum Java version taken from: https://blog.sonarsource.com/cognitive-complexity-because-testability-understandability
  9. Miller‘s law Photo by juan pablo rodriguez on Unsplash

  10. Abstraction Photo by Jaciel Melnik on Unsplash

  11. The sound of poison has gone off Our traps aren't

    malleable, beware! No dogma or deterrent can dissuade us from our torment path Said path has run nigh on eight years Dark skills in trade, we hone and sharpen arrow blade The trap is laid While we await and wait and wait Laying Traps Crippled Black Phoenix 2012 Photo by Mario Azzi on Unsplash
  12. Neglecting the costs Photo by Marissa Rodriguez on Unsplash

  13. Example: Scrapping your boilerplate with too much passion

  14. Choose your poison Photo by Kylli Kittus on Unsplash

  15. Embracing industry standards Photo by Samuel Zeller on Unsplash

  16. Embracing industry standards the latest fad Photo by Shawn McKay

    on Unsplash
  17. Example: Abstracting over monads. Everywhere. All the time.

  18. Use cases • Library development • Need for multiple interpreters

    in your program
  19. Uses in application development def showArticlePage(id: Long): Task[ArticlePage] = for

    { article <- fetchArticle(id) comments <- if (article.commentingAllowed) fetchComments(id) else Task.now(Seq.empty) } yield ArticlePage(article, comments) def showArticlePage(id: Long): Free[Effect, ArticlePage] = for { article <- fetchArticle(id) comments <- if (article.commentingAllowed) fetchComments(id) else Free.pure[Effect, Seq[Comment]](Seq.empty) } yield ArticlePage(article, comments)
  20. The costs of abstraction

  21. Foregoing abstraction Photo by Markus Spiske on Unsplash

  22. Functional core, imperative shell Photo by Kelly Sikkema on Unsplash

  23. Functional core, imperative shell // imperative shell: def showArticlePage(id: Long):

    Task[ArticlePage] = for { article <- fetchArticle(id) comments <- commentingAllowed(article) .fold(_ => Task.now(Seq.empty), _ => fetchComments(id)) } yield ArticlePage(article, comments) // functional core: def commentingAllowed(article: Article): Either[String, Unit] = { if (article.commentingAllowed) Right(()) else Left("Commenting not allowed") }
  24. Discourse • Focuses on how instead of why (or why

    not) • Emphasis on more and more advanced techniques
  25. Let’s write more about trade-offs

  26. Let’s write more about boring solutions

  27. Maximum zoom Photo by Drew Graham on Unsplash

  28. Example: Microservices with frontend service

  29. We jump towards technological solutions too quickly

  30. aim42 • Architecture improvement method • Iteratively in three phases:

    – Analyse – Evaluate – Improve • https://aim42.org/
  31. Root cause analysis

  32. … applied to the latency problem • Why is our

    overall request latency so high? • Why do we need to make so many requests? • Why are our microservices designed around a single entity? • What if … ?
  33. What if … ?

  34. … applied to the JSON codecs • Why do we

    provide a JSON API? • Why do we have a single page app?
  35. What if … • we did server-side rendering of HTML?

    – No JSON API – No duplicated business logic – Much simpler frontend JS code https://medium.com/@jmanrubia/escaping-the-spa-rabbit-hole-with-turbolinks-903f942bf52c
  36. We love (functional) programming too much

  37. Coding ~ Comfort zone Photo by Marcos Gabarda on Unsplash

  38. What does it mean to be a software developer?

  39. Conclusions • Leave your programmer comfort zone • Consider costs

    • Talk about trade-offs • Don’t ignore boring solutions • Slow down & stop meddling with symptoms
  40. Thanks for your attention! Questions? Daniel Westheide @kaffeecoder