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

Easy and complex

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Easy and complex

Avatar for Sobolev Nikita

Sobolev Nikita

February 08, 2020
Tweet

More Decks by Sobolev Nikita

Other Decks in Programming

Transcript

  1. 4

  2. 6

  3. "Я не знаю, что делает функция на 500 строк, но

    могу сразу сказать, что она слишком сложная" – Неизвестный автор 8
  4. M = E − N + 2P M = цикломатическая

    сложность, E = количество рёбер в графе, N = количество узлов в графе, P = количество компонент связности. 12
  5. int sumOfPrimes(int max) { // +1 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; } 14
  6. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { if (i % j == 0) { continue OUT; } } total += i; } return total; } 15
  7. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { // +1 if (i % j == 0) { continue OUT; } } total += i; } return total; } 16
  8. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { // +1 if (i % j == 0) { // +1 continue OUT; } } total += i; } return total; } // Cyclomatic Complexity 4 17
  9. String getWords(int number) { // +1 switch (number) { case

    1: // +1 return "one"; case 2: // +1 return "a couple"; case 3: // +1 return "several"; default: return "lots"; } } // Cyclomatic Complexity 4 21
  10. int sumOfPrimes(int max) { // +1 int total = 0;

    OUT: for (int i = 1; i <= max; ++i) { // +1 for (int j = 2; j < i; ++j) { // +1 if (i % j == 0) { // +1 continue OUT; } } total += i; } return total; } // Cyclomatic Complexity 4 22
  11. 25

  12. > Increment when there is a break in the linear

    (top-to-bottom, left- to-right) flow of the code 25
  13. > Increment when there is a break in the linear

    (top-to-bottom, left- to-right) flow of the code > Increment when structures that break the flow are nested 25
  14. > Increment when there is a break in the linear

    (top-to-bottom, left- to-right) flow of the code > Increment when structures that break the flow are nested > Ignore "shorthand" structures that readably condense multiple lines of code into one 25
  15. // Cyclomatic Cognitive String getWords(int number) { // +1 switch

    (number) { // +1 case 1: // +1 return "one"; case 2: // +1 return "a couple"; default: // +1 return "lots"; } } // =4 =1 26
  16. // Cyc Cog int sumOfPrimes(int max) { // +1 int

    total = 0; OUT: for (int i = 1; i <= max; ++i) { // +1 +1 for (int j = 2; j < i; ++j) { // +1 +2 (nesting=1) if (i % j == 0) { // +1 +3 (nesting=2) continue OUT; // +1 } } total += i; } return total; } // =4 =7 27
  17. String getWords(int number) { switch (number) { case 1: return

    "one"; case 2: return "a couple"; case 3: return "several"; default: return "lots"; } } Просто 28
  18. 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; } Сложно 29
  19. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 36
  20. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 37
  21. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 38
  22. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 39
  23. <_ast.Module> ┗━ body ┣━ [0] <_ast.Expr> ┃ ┗━ value: <_ast.Call>

    ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: first_long_name_with_meaning ┃ ┃ ┣━ [1] <_ast.Name> ┃ ┃ ┃ ┗━ id: second_very_long_name_with_meaning ┃ ┃ ┗━ [2] <_ast.Name> ┃ ┃ ┗━ id: third ┃ ┗━ func: <_ast.Name> ┃ ┗━ id: print 40
  24. <_ast.Module> ┗━ body ┗━ [1] <_ast.Expr> ┗━ value: <_ast.Call> ┣━

    args ┃ ┣━ [0] <_ast.BinOp> ┃ ┃ ┣━ left: <_ast.BinOp> ┃ ┃ ┃ ┣━ left: <_ast.Name> ┃ ┃ ┃ ┃ ┗━ id: first ┃ ┃ ┃ ┣━ op: <_ast.Mult> ┃ ┃ ┃ ┗━ right: <_ast.Constant> ┃ ┃ ┃ ┗━ value: 5 ┃ ┃ ┣━ op: <_ast.Add> ┃ ┃ ┗━ right: <_ast.BinOp> ┃ ┃ ┣━ left: <_ast.Attribute> ┃ ┃ ┃ ┣━ attr: pi ┃ ┃ ┃ ┗━ value: <_ast.Name> ┃ ┃ ┃ ┗━ id: math ┃ ┃ ┣━ op: <_ast.Mult> ┃ ┃ ┗━ right: <_ast.Constant> ┃ ┃ ┗━ value: 2 ┃ ┣━ [1] <_ast.Call> ┃ ┃ ┣━ args ┃ ┃ ┃ ┗━ [0] <_ast.Starred> ┃ ┃ ┃ ┗━ value: <_ast.Name> ┃ ┃ ┃ ┗━ id: matrix ┃ ┃ ┗━ func: <_ast.Attribute> ┃ ┃ ┗━ value: <_ast.Name> ┃ ┃ ┗━ id: matrix ┃ ┗━ [2] <_ast.Call> ┃ ┣━ args ┃ ┃ ┣━ [0] <_ast.Name> ┃ ┃ ┃ ┗━ id: matrix ┃ ┃ ┗━ [1] <_ast.Constant> ┃ ┃ ┗━ value: 2 ┃ ┗━ func: <_ast.Attribute> ┃ ┣━ attr: show ┃ ┗━ value: <_ast.Name> ┃ ┗━ id: display ┗━ func: <_ast.Name> ┗━ id: print 42
  25. Структуры > OverusedExpressionViolation > OverusedStringViolation > TooLongYieldTupleViolation > TooLongCompareViolation >

    TooLongTryBodyViolation > TooDeepAccessViolation > TooLongCallChainViolation > TooDeepNestingViolation > TooManyConditionsViolation > TooManyElifsViolation > TooManyForsInComprehensionViolation > TooManyExceptCasesViolation 48
  26. 53

  27. Процесс: > Пишем простые блоки кода > В какой-то момент

    сложность переполняется > Рефакторим 55
  28. -- the type of monad m data m a =

    ... -- return takes a value and embeds it in the monad. return :: a -> m a -- bind is a function that combines m a with a computation -- monad instance m b (>>=) :: m a -> (a -> m b) -> m b
  29. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 67
  30. Насколько сложна ваша предметная область? > Порог входа > Термины

    и их количество > Процессы > Правила > Варианты использования 74
  31. 76

  32. 78

  33. 79

  34. 80

  35. sum : (single : Bool) -> isSingleton single -> Nat

    sum True x = x sum False [] = 0 sum False (x :: xs) = x + sum False xs 83
  36. 84

  37. Callable[[List[int]], List[str]] • Выбросит ли она исключение? • Является ли

    она чистой? • Будет ли добиться в базу или http? 88
  38. Callable[[List[int]], List[str]] • Выбросит ли она исключение? Да? Result[List[int], Exception]

    • Является ли она нечистой? Да? IO[List[int]] • Все вместе? Конечно! IO[Result[List[int], Exception]] 89
  39. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 90
  40. def fetch_user_profile(user_id: int) -> IOResultE['User']: def _make_request( user_id: int, )

    -> IOResultE[requests.Response]: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response def _parse_json( response: requests.Response, ) -> ResultE['User']: return response.json() 91
  41. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 92
  42. def fetch_user_profile(user_id: int) -> IOResultE['User']: return flow( user_id, _make_request, IOResult.lift_result(bind(_parse_json)),

    ) @impure_safe def _make_request(user_id: int) -> requests.Response: response = requests.get('/users/{0}'.format(user_id)) response.raise_for_status() return response @safe def _parse_json(response: requests.Response) -> 'User': return response.json() 93
  43. values := << 1, 2, 4, 8 >> -- Sum

    of (index * values [index]). across values as i from sum := 0 loop sum := sum + i.cursor_index * i.item end 96
  44. @deal.pre(lambda *args: all(arg > 0 for arg in args)) @deal.post(lambda

    result: result > 5) def sum_positive(*args): return sum(args) sum_positive(1, 2, 3, 4) # 10 sum_positive(1, 2, -3, 4) # PreContractError: sum_positive(1, 2) # PostContractError: 98
  45. @deal.inv(lambda post: post.likes >= 0) class Post: likes = 0

    post = Post() post.likes = 10 post.likes = -10 # InvContractError: 99
  46. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 105
  47. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 106
  48. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 107
  49. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 108
  50. [importlinter] root_package = django_project include_external_packages = True [importlinter:contract:layers] name =

    Layered architecture of our linter type = layers containers = django_project layers = urls views forms models logic 109
  51. [importlinter:contract:violation-independence] name = Independence contract for violations type = independence

    modules = django_project.billing_app django_project.auth_app django_project.orders_app django_project.statistics_app 111
  52. [importlinter:contract:violation-independence] name = Independence contract for violations type = independence

    modules = django_project.billing_app django_project.auth_app django_project.orders_app django_project.statistics_app 112
  53. [importlinter:contract:violation-independence] name = Independence contract for violations type = independence

    modules = django_project.billing_app django_project.auth_app django_project.orders_app django_project.statistics_app 113
  54. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 115
  55. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 116
  56. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 117
  57. [importlinter:contract:api-restrictions] name = Forbids to import anything from dependencies type

    = forbidden source_modules = django_project.logic forbidden_modules = # Important direct and indirect dependencies: django rest_framework 118
  58. Что делать то? > Использовать DDD > Строить чистую архитектуру

    > Следить за качеством абстракций > Документировать и создавать обучающие материалы 124
  59. 127

  60. 128

  61. 132

  62. 133

  63. 135

  64. Полезные ссылки > sobolevn.me/2019/10/complexity- waterfall > github.com/wemake-services/wemake- python-styleguide > wemake-python-stylegui.de/en/latest/

    pages/usage/violations/complexity.html > sobolevn.me/2019/02/python-exceptions- considered-an-antipattern 143
  65. Полезные ссылки > sobolevn.me/2019/10/complexity- waterfall > github.com/wemake-services/wemake- python-styleguide > wemake-python-stylegui.de/en/latest/

    pages/usage/violations/complexity.html > sobolevn.me/2019/02/python-exceptions- considered-an-antipattern > github.com/dry-python/returns 143