Technical Practices for Sustainable Code 2019-09-14

5b92c5ea42bc2c2ab28d973d5da63cd6?s=47 Quinn
September 14, 2019

Technical Practices for Sustainable Code 2019-09-14

Highly maintainable code is hard to produce.
Object oriented code was supposed to enable us to write clean, understandable code - and yet... we're often stuck with incomprehensible code we can't get tests around. Most of this isn't good object oriented code.
Good object oriented code is maintainable.
Writing good object oriented code is hard.
Without maintainability we can not have sustainable code, it will rot and have to be re-written. As an industry we're often given principles (DRY, SOLID, Abstraction, Code Reuse...) which sound great to have in out object oriented code. Knowing the principles is clearly not enough to enable us to write good object oriented code. We need some hands on keyboard deliberate practices that help us get there.
I just happen to write my code using some technical practices that drive good object-oriented code.
This talk goes into a few of technical practices I've found to have the biggest impact towards good object oriented code. Strictly applying just these practices will help the maintainability of the code.
These technical practices produce simple, understandable, testable code which results in extremely maintainable code that we can work in to consistently and quickly deliver value to our customers.

5b92c5ea42bc2c2ab28d973d5da63cd6?s=128

Quinn

September 14, 2019
Tweet

Transcript

  1. Feature Parity in 25% of the Dev Hours with Technical

    Practices for Sustainable Code @TheQuinnGil QuinnGil.com
  2. Feature Parity in 25% of the Dev Hours @TheQuinnGil QuinnGil.com

  3. @TheQuinnGil QuinnGil.com Technical Practices for Sustainable Code

  4. Quinn Gil Software Development Coach International Speaker on Software Development

    Past 15 years focused on High Quality code Blog at QuinnGil.com @TheQuinnGil QuinnGil.com
  5. This is for you. @TheQuinnGil QuinnGil.com

  6. Sustainable Code through Technical Practices @TheQuinnGil QuinnGil.com

  7. Sustainable Code through Technical Practices @TheQuinnGil QuinnGil.com

  8. Sustainable Code through Technical Practices @TheQuinnGil QuinnGil.com

  9. Tests @TheQuinnGil QuinnGil.com

  10. Sustainable Code through Technical Practices @TheQuinnGil QuinnGil.com • No Getters

    / No Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection
  11. Sustainable Code through Technical Practices • No Getters / No

    Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection No Getters / No Setters @TheQuinnGil QuinnGil.com
  12. Technical Practices - No Getters - Defined class FizzBuzzBag {

    public int Input { get; set; } public string Result { get; set; } } class FizzBuzzBag: def __init__(self): self._result = None self._input = None @property def result(self): return self._result @result.setter def result(self, value): self._result = value @property def input(self): return self._input @input.setter def input(self, value): self._input = value class FizzBuzzBag{ private int input; private String result; public String getResult(){ return result; } public void setResult(String value){ result = value; } public int getInput(){ return input; } public void setInput(int value){ input = value; } } class FizzBuzzBag attr_accessor :input attr_accessor :result end @TheQuinnGil QuinnGil.com
  13. Technical Practices - No Getters No Getters? No Way! @TheQuinnGil

    QuinnGil.com
  14. No Getters Technical Practices @TheQuinnGil QuinnGil.com

  15. Technical Practices - No Getters Why No Getters? @TheQuinnGil QuinnGil.com

  16. Technical Practices - No Getters How? @TheQuinnGil QuinnGil.com

  17. Technical Practices - No Getters - How? The Class Does

    It @TheQuinnGil QuinnGil.com
  18. Technical Practices - No Getters public class FizzBuzz{ public int

    Input { get; set; } public string Result { get; set; } } public static class FizzBuzzUtils{ public static void Calculate(FizzBuzz fb){ if (fb.Input % 3 == 0){ fb.Result = "Fizz"; } if (fb.Input % 5 == 0){ if (fb.Result == null){ fb.Result = "Buzz"; } else { fb.Result += "Buzz"; } } if (string.IsNullOrEmpty(fb.Result)){ fb.Result = fb.Input.ToString(); } } } @TheQuinnGil QuinnGil.com
  19. Technical Practices - No Getters / No Setters public class

    FizzBuzz{ private readonly int _input; public FizzBuzz(int input) => _input = input; public string Result(){ string result = null; if (_input % 3 == 0) result = "Fizz"; if (_input % 5 == 0){ if (result == null) result = "Buzz"; else result += "Buzz"; } if (string.IsNullOrEmpty(result)){ result = _input.ToString(); } return result; } } public class FizzBuzz{ public int Input { get; set; } public string Result { get; set; } } public static class FizzBuzzUtils{ public static void Calculate(FizzBuzz fb){ if (fb.Input % 3 == 0){ fb.Result = "Fizz"; } if (fb.Input % 5 == 0){ if (fb.Result == null){ fb.Result = "Buzz"; } else { fb.Result += "Buzz"; } } if (string.IsNullOrEmpty(fb.Result)){ fb.Result = fb.Input.ToString(); } } } @TheQuinnGil QuinnGil.com
  20. Technical Practices - No Getters / No Setters Benefits @TheQuinnGil

    QuinnGil.com
  21. Sustainable Code through Technical Practices No Getters / No Setters

    @TheQuinnGil QuinnGil.com • No Getters / No Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection Sustainable Code from Encapsulating Behavior
  22. Sustainable Code through Technical Practices `if` Only As Guard Clause

    @TheQuinnGil QuinnGil.com • No Getters / No Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection
  23. Technical Practices - `if` Only As Guard Clause Guard Clause

    Only? No Way!
  24. Technical Practices - `if` Only As Guard Clause Default Path

    @TheQuinnGil QuinnGil.com
  25. `if` Only As Guard Clause - Default Path public class

    FizzBuzz{ private readonly int _input; public FizzBuzz(int input) => _input = input; public string Result(){ string result = null; if (_input % 3 == 0) result = "Fizz"; if (_input % 5 == 0){ if (result == null) result = "Buzz"; else result += "Buzz"; } if (string.IsNullOrEmpty(result)){ result = _input.ToString(); } return result; } } @TheQuinnGil QuinnGil.com
  26. public class FizzBuzz{ private readonly int _input; public FizzBuzz(int input)

    => _input = input; public string Result(){ string result = null; if (_input % 3 == 0) result = "Fizz"; if (_input % 5 == 0){ if (result == null) result = "Buzz"; else result += "Buzz"; } if (string.IsNullOrEmpty(result)){ result = _input.ToString(); } return result; } } `if` Only As Guard Clause - Default Path public class FizzBuzz{ private int _input; public FizzBuzz(int input) => _input = input; public string Result(){ if (_input % 15 == 0) return "FizzBuzz"; if (_input % 3 == 0) return "Fizz"; if (_input % 5 == 0) return "Buzz"; return _input.ToString(); } } @TheQuinnGil QuinnGil.com
  27. Technical Practices - `if` Only As Guard Clause Cascading Changes

    @TheQuinnGil QuinnGil.com
  28. Technical Practices - `if` Only As Guard Clause Cascading Changes

    switch and else - always evil @TheQuinnGil QuinnGil.com
  29. Technical Practices - `if` Only As Guard Clause How? @TheQuinnGil

    QuinnGil.com
  30. `if` Only As Guard Clause - Default Path public class

    FizzBuzz{ private int _input; public FizzBuzz(int input) => _input = input; public string Result(){ if (_input % 15 == 0) return "FizzBuzz"; if (_input % 3 == 0) return "Fizz"; if (_input % 5 == 0) return "Buzz"; return _input.ToString(); } } public class FizzBuzz{ private readonly int _input; public FizzBuzz(int input) => _input = input; public string Result(){ string result = null; if (_input % 3 == 0) result = "Fizz"; if (_input % 5 == 0){ if (result == null) result = "Buzz"; else result += "Buzz"; } if (string.IsNullOrEmpty(result)){ result = _input.ToString(); } return result; } } @TheQuinnGil QuinnGil.com
  31. Sustainable Code through Technical Practices `if` Only As Guard Clause

    @TheQuinnGil QuinnGil.com • No Getters / No Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection Sustainable Code from Limited Branching; Smaller Methods; Encapsulated Behavior Decisions
  32. Sustainable Code through Technical Practices Isolate Their Code @TheQuinnGil QuinnGil.com

    • No Getters / No Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection
  33. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { HttpClient httpClient = new HttpClient(); HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  34. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { //COUPLING HttpClient httpClient = new HttpClient(); HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  35. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { //COUPLING HttpClient httpClient = new HttpClient(); //EXCEPTION HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  36. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { //COUPLING HttpClient httpClient = new HttpClient(); //EXCEPTION HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); //EXCEPTION, COUPLING JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  37. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { //COUPLING HttpClient httpClient = new HttpClient(); //EXCEPTION HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); //EXCEPTION, COUPLING JObject jsonContent = JObject.Parse(readAsStringAsync); //IMPERATIVE string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  38. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { //COUPLING HttpClient httpClient = new HttpClient(); //EXCEPTION HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); //EXCEPTION, COUPLING JObject jsonContent = JObject.Parse(readAsStringAsync); //IMPERATIVE string value = jsonContent.Value<string>("some_key"); //EXCEPTION return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  39. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { HttpClient httpClient = new HttpClient(); HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  40. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { IHttpClientBookEnd httpClient = new HttpClientFacade(); HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  41. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { IHttpClientBookEnd httpClient = new HttpClientFacade(); IHttpResponseMessageBookEnd httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  42. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { IHttpClientBookEnd httpClient = new HttpClientFacade(); IHttpResponseMessageBookEnd httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.ContentAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  43. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { IHttpResponseMessageBookEnd httpResponseMessage = await _httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.ContentAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  44. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { IHttpResponseMessageBookEnd httpResponseMessage = await _httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.ContentAsStringAsync(); IParsedObject parsedObject = _parser.Parse(readAsStringAsync); string value = parsedObject.StringValue("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  45. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { IHttpResponseMessageBookEnd httpResponseMessage = await _httpClient.SendAsync(r); IParsedObject parsedObject = await httpResponseMessage.ParsedContent(_parser); string value = parsedObject.StringValue("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  46. Technical Practices - Isolate Their Code - 3rd Party public

    async Task<string> NetworkCall(HttpRequestMessage r) { HttpClient httpClient = new HttpClient(); HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(r); string readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync(); JObject jsonContent = JObject.Parse(readAsStringAsync); string value = jsonContent.Value<string>("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } public async Task<string> NetworkCall(HttpRequestMessage r) { IHttpResponseMessageBookEnd httpResponseMessage = await _httpClient.SendAsync(r); IParsedObject parsedObject = await httpResponseMessage.ParsedContent(_parser); string value = parsedObject.StringValue("some_key"); return value.Contains("expected_Value") ? "Success" : "Failure"; } @TheQuinnGil QuinnGil.com
  47. What Else? Technical Practices - Isolate Their Code @TheQuinnGil QuinnGil.com

  48. Operating System Technical Practices - Isolate Their Code @TheQuinnGil QuinnGil.com

  49. How? Technical Practices - Isolate Their Code @TheQuinnGil QuinnGil.com

  50. Technical Practices - Isolate Their Code - How? HttpClient httpClient

    = new HttpClient(); @TheQuinnGil QuinnGil.com IHttpClientBookEnd httpClient = new HttpClientFacade();
  51. Technical Practices - Isolate Their Code - How? internal class

    HttpClientFacade : IHttpClientBookEnd { private static HttpClient _testClient; #if DEBUG public static void SetTestClient(HttpClient testClient) => _testClient = testClient; #endif private readonly HttpClient _httpClient; public HttpClientFacade() : this(new HttpClient()) { } private HttpClientFacade(HttpClient httpClient) => _httpClient = httpClient; public async Task<IHttpResponseMessageBookEnd> SendRequestAsync(HttpRequestMessage msg) => new HttpResponseMessageAdapter(await HttpClient().SendAsync(msg)); private HttpClient HttpClient() => _testClient ?? _httpClient; } @TheQuinnGil QuinnGil.com
  52. Technical Practices - Isolate Their Code - How? internal class

    HttpClientFacade : IHttpClientBookEnd { private static HttpClient _testClient; #if DEBUG public static void SetTestClient(HttpClient testClient) => _testClient = testClient; #endif private readonly HttpClient _httpClient; public HttpClientFacade() : this(new HttpClient()) { } private HttpClientFacade(HttpClient httpClient) => _httpClient = httpClient; public async Task<IHttpResponseMessageBookEnd> SendRequestAsync(HttpRequestMessage msg) => new HttpResponseMessageAdapter(await HttpClient().SendAsync(msg)); private HttpClient HttpClient() => _testClient ?? _httpClient; } @TheQuinnGil QuinnGil.com
  53. Technical Practices - Isolate Their Code - How? internal class

    HttpClientFacade : IHttpClientBookEnd { private static HttpClient _testClient; #if DEBUG public static void SetTestClient(HttpClient testClient) => _testClient = testClient; #endif private readonly HttpClient _httpClient; public HttpClientFacade() : this(new HttpClient()) { } private HttpClientFacade(HttpClient httpClient) => _httpClient = httpClient; public async Task<IHttpResponseMessageBookEnd> SendRequestAsync(HttpRequestMessage msg) => new HttpResponseMessageAdapter(await HttpClient().SendAsync(msg)); private HttpClient HttpClient() => _testClient ?? _httpClient; } @TheQuinnGil QuinnGil.com
  54. Technical Practices - Isolate Their Code - How? internal class

    HttpClientFacade : IHttpClientBookEnd { private static HttpClient _testClient; #if DEBUG public static void SetTestClient(HttpClient testClient) => _testClient = testClient; #endif private readonly HttpClient _httpClient; public HttpClientFacade() : this(new HttpClient()) { } private HttpClientFacade(HttpClient httpClient) => _httpClient = httpClient; public async Task<IHttpResponseMessageBookEnd> SendRequestAsync(HttpRequestMessage msg) => new HttpResponseMessageAdapter(await HttpClient().SendAsync(msg)); private HttpClient HttpClient() => _testClient ?? _httpClient; } @TheQuinnGil QuinnGil.com
  55. User Interface Technical Practices - Isolate Their Code @TheQuinnGil QuinnGil.com

  56. Sustainable Code through Technical Practices Isolate Their Code @TheQuinnGil QuinnGil.com

    • No Getters / No Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection Sustainable Code from Full Testability; Narrow Scope of Change; Reduced Complexity
  57. Sustainable Code through Technical Practices @TheQuinnGil QuinnGil.com • No Getters

    / No Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection
  58. Feature Parity in 25% of the Dev Hours @TheQuinnGil QuinnGil.com

  59. THANK YOU! @TheQuinnGil QuinnGil.com

  60. Sustainable Code through Technical Practices • No Getters / No

    Setters • `if` Only As Guard Clause • Isolate Their Code • Never `null` • No `new` inline • Composition, Not Inheritance • <Strong opinions loosely held> • Be Immutable • No Primitives • Extract Cohesion • No Public Statics • Never Reflection @TheQuinnGil QuinnGil.com