Хибна абстракція

12 липня, 2020
Обкладинка статті

Фото від NeonBrand

Я роздумувала про "хибну абстракцію". Моя доповідь на RailsConf у 2014-му "all the little things" містила секцію в якій я заявила:

дублювання набагато дешевше за хибну абстракцію

І в підсумку, я дала пораду:

надавайте перевагу дублюванню над хибною абстракцією

Ця невелика частина великої доповіді викликала на диво сильну реакцію. Побутувала думка що я втратила розум, та небагато більше людей поміж рядків виразили свою підтримку.

Сила реакції заставила мене зрозуміти наскільки розповсюдженою і непіддатливою є проблема "хибної абстракції". Я почала ставити питання і наступна схема почала вимальовуватись:

  1. Програміст A помічає дублювання.
  2. Програміст A виносить дублюванню та дає йому ім'я.

    Створюється нова абстракція. Це може бути новий метод, або навіть новий клас.

  3. Програміст A замінює дублювання на нову абстракцію.

    Ох, код — ідеальний. Програміст A щасливо віддаляється.

  4. Проходить час.
  5. З'являється нова вимога для якої поточна абстракція майже ідеально підходить.
  6. Програміст Б отримує завдання реалізувати цю вимогу.

    Програміст Б вважає за честь залишити існуючу абстракцію, але оскільки поведінка не повністю ідентична, він змінює код щоб приймати параметр, та потім додає логіку щоб умовно виконувати правильну дію залежно від параметра.

    Те що раніше було універсальною абстракцією зараз поводиться по різному у різних випадках.

  7. З'являється інша вимога.

    Програміст X.
    Інший додатковий параметр.
    Інша нова умова.
    Продовжується допоки реалізація стає неясною.

  8. Десь тут у цій історії з'являєшся ти й твоє життя драматично погіршується

Існуючий код має великий вплив. Саме його існування стверджує що він правильний та необхідний. Ми знаємо, що код виражає витрачені зусилля, і ми дуже вмотивовані зберігати цінність цих зусиль. Та, на жаль, гірка правда в тому що чим складнішим та незрозумілішим є код, іншими словами, чим більше вкладено в його створення, тим більше тиску ми відчуваємо щоб залишити його ("омана безповоротних витрат"). Це ніби наша підсвідомість говорить нам: "Боже, це настільки заплутано, що, певно, потрібні були віки, щоб вийшло правильно. Без сумніву, це дійсно дуже важливо. Марнування цих зусиль було б гріхом."

Коли ти з'являєшся на 8-му кроці цієї історії, цей тиск може змусити тебе рухатись вперед, а саме, реалізувати нову вимогу змінюючи існуючий код. Спроба зробити це, однак, є жорстокою. Код більше не виражає єдину, типову абстракцію, але натомість став обтяженою умовами процедурою що переплітається з рядом нечітко пов'язаних ідей. Його важко зрозуміти та легко зламати.

Якщо ви опинилися в такій ситуації, опирайтеся керуванню спричиненому безповоротними витратами. Працюючи з хибною абстракцією найшвидший шлях вперед — це назад. Робіть наступне:

  1. Поверніть дублювання, вставляючи абстрагований код назад в кожне місце виклику.
  2. В місці виклику, використовуйте параметри що передаються щоб виявити підмножину вставленого коду що це конкретне місце виконує.
  3. Видаліть частини що не потребуються в цьому конкретному місці.

Цей підхід усуває обох: абстракцію та умови, до того ж зменшує кожен виклик до тільки того коду що дійно необхідний. Коли ви таким чином відмотуєте рішення, часто виявляється, що хоча кожне місце виклику нібито посилалось на спільну абстракцію, код що виконувався був доволі унікальним. Після того як стару абстракцію повністю видалено можна почати заново, повторно ізолюючи дублювання та виокремлюючи абстракції.

Якось я спостерігала як люди намагались доблесно рухатись вперед з хибною абстракцією, але вони не досягли значного успіху. Додавання нових функцій було неймовірно складним і кожна успішна інтеграція ускладнювала код, що робила додавайння наступних функцій іще важчим. Тоді вони зміни свою точку зору з "Я мушу зберегти наш внесок в цей код" на "У цьому коді був певний сенс та, либонь, ми дізнались від нього все що могли" та давали собі дозвіл переглянути свої абстракції у світлі поточних вимог, все стало простішим. Коли вони вбудували код, шлях вперед став очевидним, та додавання функцій стало швидшим та простішим.

Мораль історії? Не потрапляйте в пастку омани безповоротних витрат. Якщо ви зловили себе на передаванні параметрів та додаванні умовних розгалужень у спільний код, абстракція не є правильною. Можливо почати з неї було хорошим рішення, та цей день минув. Як тільки абстракція виявилась хибною найкраща стратегія — повернути дублювання і дозволити йому показати вам правильний шлях. Хоча іноді є сенс назбирати кілька умов, щоб зрозуміти, що відбувається. Ви менше страждатимете, якщо відмовитеся від хибної абстракції раніше, а не пізніше.

Коли абстракція неправильна, найшвидший шлях вперед — це назад. Це не відступ, це рух у кращому напрямку. Зробіть це. Ви покращите своє власне життя та життя всіх тих хто прийде за вами.