Как работать с легаси кодом
Перейти к содержимому

Как работать с легаси кодом

  • автор:

Working with Legacy Code

Sooner or later, in your career as a software engineer, you are going to have to deal with Legacy Code. In this post, I’m going to illustrate some of the challenges I faced working with Legacy Code and some of the strategies that helped me deal with it.

What is Legacy Code anyway?

Let’s start by clarifying what Legacy Code is. There are a few different definitions but, for the sake of this post, I’m going to define Legacy Code as:

  1. Source code inherited from someone else.
  2. Source code without tests.

Each one of the above definitions focuses on a particular aspect of Legacy Code. In particular:

  1. Code inherited from someone else could be very hard to understand because of missing documentation and/or lack of meaningful variable and method names. This is even more challenging if whoever wrote the original code is not around anymore and nobody in your team has deep knowledge of the existing codebase.
  2. The lack of tests makes Legacy Code difficult to work with because:
    • Well written tests give developers confidence that changing the code won’t introduce bugs (because that should make tests fail). In a codebase without a meaningful amount of tests it’s really easy to introduce bugs that go unnoticed until users start complaining about them.
    • Tests also provide an informal documentation for source code, as they show how classes and methods are used. The lack of tests, then, can be seen as another aspect of missing documentation.

Other common issues with Legacy Code include:

  • No coding standards enforced.
  • No separation of concerns.
  • Extensive use of anti-patterns (the most popular is the abuse of singletons).
  • Extensive copypasta (lack of DRYness).

The above issues create a spaghetti code scenario where the lack unit tests is likely caused, among other things, by:

  • Untestable business logic.
  • Insufficient abstraction.
  • Absence of dependency injection.

How to work with confidence with Legacy Code

Now that we clarified what we define as Legacy Code, I’m going to illustrate some of the strategies that helped me deal with it over the years.

How can we mitigate the risk of introducing bugs when modifying Legacy Code and/or when adding new features on top of it? The “by the book” suggestion to work with Legacy Code can be summarized as: “write tests for the code you are about to change before you actually start changing it”. This is a great suggestion and, if your team has enough resources to spare, you should definitely write tests before changing the code.

But what should you do if you find yourself in the situation where you can’t delay a new feature by a few weeks to add missing tests? If you’re just changing some business logic, there’s a possibility that you can get a reasonable code coverage just by writing a few tests. The likelihood of this scenario is related to how localized are the changes you’re planning to make. If you’re considering changing a single class, chances are good that you are in the above scenario. If your planned changes, instead, involve modifying many classes it’s likely that you won’t be able to achieve a reasonable code coverage in a short amount of time.

Let’s focus on a rather common and even more challenging scenario: Your task is to build an entirely new feature with a brand new UI and functionality that is going to replace the current legacy UI. I’m going to assume that, as it usually happens, adding the missing unit and UI tests before starting on the new code is not feasible. What could you do complete your task with the reasonable confidence that you’re not introducing lots of bugs?

Feature flags to the rescue

A strategy that worked really well in my experience is to use feature flags. If you make sure to add all new code behind a dedicated feature flag you can build the new feature with a much improved confidence of not introducing lots of bugs. In particular, by adopting a feature flag you can easily:

  • Compare the new app behavior (feature flag enabled) and the old app behavior (feature flag disabled). This is really useful both during development and manual testing to make sure you are building the right functionality.
  • Turn on and off the feature flag once you release the new app version (this assumes you have a way to fetch feature flags from the network). This allows to easily fallback on the previous functionality in case your users are experiencing issues with the new feature (or in case your new feature requires some additional work).
  • Easily track the obsolete code to be removed once the new feature is fully functional and tested.

This can be summarized as:

The firewall refactoring technique
We build the new implementation in parallel and once its fully functional
we tear down the old one and make the switch!

Legacy Code and Tech Debt

Legacy Code can be (and I think it should be) considered Tech Debt. This is because making changes inside Legacy Code is way more difficult and time consuming that writing code from scratch. But we should also not underestimate the tendency of even brand new code to become Legacy Code and turn into Tech Debt. One strategy that worked really well for me in the past is to create a team practice to dedicate a fixed amount of development time to address Tech Debt. There’s likely no one size fits all approach for this but, for instance, allocating an entire quarter or a few sprints throughout the year to reduce Tech Debt should go a long way. After all, shouldn’t we include Continuous Refactoring anyway as part of our Agile practices?

How do you fix Legacy Code?

As we plan to refactor portions of our Legacy Code, what could we do to maximize the effectiveness of our effort? Answering this question is particularly important within codebases with a large amount of Legacy Code, when we feel the task is daunting. I’m going to answer to the above question by paraphrasing a well know book:

How do you fix Legacy Code? One class at a time!

In addition to the above, I also think the safest (and likely most effective) way to refactor old code is by adopting a bottom-up size approach: Start with the smallest class first! Refactoring small classes first should allow you to make progress faster as you can start testing the new behavior a little bit at a time. Leveraging a feature flag (as discussed earlier) is going to help quite a bit through the entire process since you will be able to keep the old code around until the entire functionality has been refactored and fully tested.

To summarize

The above illustrated approach to working with Legacy Code can be summarized with the following recipe:

The Engineer's Complete Guide to Legacy Code

Cate Lawrence

We live in a world where progress is king — complete tasks, meet goals, commit more, deploy frequently, ship faster. But what happens when something is lurking in the codebase that’s holding you back? That’s right, legacy code. Let’s take a look at how you can deal with legacy code, once and for all.

This guide will cover the following topics��

  • What is legacy code?
  • The characteristics of legacy code
  • The problem of legacy code and technical debt
  • Tips and tools for working effectively with legacy code
  • Legacy code migration and cultural shift
  • Resources for dealing with legacy code

What is legacy code?

When we talk about legacy code, your first thought might be of old, obsolete code. Yes, it may be written for operating systems that are no longer supported. However, legacy code means code which is:

  • Inherited code: it may be from another developer, team, or older software version.
  • Code that the original developer no longer maintains.
  • A code base that is no longer engineered but continually patched.
  • Legacy code can also refer to unsupported operating systems, hardware and formats.

The characteristics of legacy code

Ionis characterises legacy code as “the antithesis of clean code which is easy to understand, maintain, and adapt. Legacy code is unwieldy, outdated, and messy, which can lead to numerous problems.” It’s typically resistant to bug finding through automated tests.

Nicolas Carlo calls legacy code code you’re not comfortable changing but also “valuable code you’re afraid to change” and “the code you need to change, and you struggle to understand.” He suggests that you may, in fact, be responsible for “that code you wrote because you can’t remember the hell you had in mind when you did. Yes, our past self often makes silly mistakes. (If you ever need a good reason for great documentation, this is it).

The problems of legacy code and technical debt

Sometimes legacy code exists for a reason. It may point to a team with a crazy high turnover, making little effort in their documentation. Or a team of more experienced developers who are comfortable with older languages. A company may keep legacy code due to competitive advantages over other businesses. They may fear that rewriting code may introduce new bugs or remove hidden functionality.

However, legacy code may also operate in a black box that is less accessible to a broader development community, meaning insufficient quality improvement, security, and maintainability. When people leave, it may be hard to find new developers appropriately skilled to work with it. As time goes on, the code may struggle to work with new and emerging apps and hardware. The company risks falling behind and is drowning in technical debt. Do you hate legacy code?

Ivo Lukač did a survey which revealed over half of the respondents would rather not work on it, and an additional 11% hate it.

Screen Shot 2019-09-30 at 15.15.51

Specifically, their pain points are many, with spaghetti code and fear of breaking something the most common concerns followed by the frustration of sticking to depreciated technology. This survey offers an insight into the complexities of dealing with legacy code and explains some of the reasons developers prioritise other work.

Intrinsic to legacy code is technical debt — each time the code changes hands, it needs more and more features and bug fixing, all of which is time-consuming and frustrating, and the amount of technical debt continues to grow.

Tips and tools for working effectively with legacy code

When working with legacy code, time and energy is precious and you need to know where to concentrate your efforts. Planning and purpose are critical — you need to know what can be changed and what to ignore. Fortunately, there’s plenty of tools available to make life easier:

Static code analysis

Static code analysis is the process of debugging by examining source code before a program is run. Code is analyzed against a set (or multiple sets) of coding rules. Then you get a diagnosis report of any violations of these rules based on their severity. This helps you prioritise the most urgent or important tasks.

In general, static code analysis is a good practice even beyond legacy code. It creates the opportunity to identify and eliminate code defects before code is pushed for functional QA, saving a lot of later pain and reducing technical debt. Some examples of static code tools:

A lot of IDEs that are used for code refactoring already use static analysis.

Code refactoring legacy code

Code refactoring is one way to deal with legacy code. It’s about overhauling code to make it more understandable, maintainable and adaptable through optimizing code, simplifying and merging variables and classes and rewriting command methods.

As Martin Fowler, author of two books on refactoring, explains:

”Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure. It is a disciplined way to clean up code that minimizes the chances of introducing bugs. In essence, when you refactor you are improving the design of the code after it has been written.”

However, you can only begin refactoring once it is clear how the code works — and doesn’t work. The code is then optimized piece by piece. Any unnecessary code is removed or rewritten. Classes and variables in the code are simplified and merged. Command methods are adapted and rewritten. In the end, refactoring is basically a general overhaul of the legacy code. The resulting code is easier to understand, maintain, and adapt.

Developers should test code after refactoring to check for any breaks. Continuous integration is useful as it enables you to revert to a previous build if something goes wrong.

Add context to your code

Stepsize help Engineering teams gain actionable insights into their technical debt, including those caused by legacy code. Engineers can track debt, add bookmarks, and organise their TODOs in their code editor, plus see the impact of technical debt and prioritise it in the webapp.

Legacy code migration and cultural shift

There’s increased interest in migrating legacy systems to low code development platforms. Sylvain Leroy wrote a succinct but in-depth guide to legacy software migration a few years back that’s worth a read. There are also practices like migrating desktop or on-premise data centres to the cloud and the shift from monoliths to microservices. While discussing all of these in great detail is beyond the scope of this article, it’s important to explore these trends in regard to cultural change and company culture:

  • How do developers collaborate?
  • How is knowledge passed down from developer to developer?
  • What efforts are made to retain knowledge when someone leaves?
  • What are the company standards for documentation and who ensures they are met?
  • How does the company prioritise time spent dealing with technical debt? What incentives do they offer (especially important if everyone hates doing it: maybe they need to provide more time off or bring in specialists?
  • How do you ensure that the new code is clean and error-free from here on? What about training and development?

How to make the case for dealing with legacy code?

Stepsize is an editor-first issue tracker for a healthy codebase that helps Engineers:

How to Effectively Refactor Legacy Code ?

Raghavan Lakshmana

In the life of a software engineer, substantial amount of time is spent on working with legacy code. A legacy code may require attention either to add a new feature, fix a bug or optimize the application. Michael Feathers in his book Working Effectively with Legacy Code outlines five important steps to improve the efficiency of refactoring legacy code,

  1. Identify the change point
  2. Find the inflection point
  3. Break dependencies
  4. Write tests
  5. Refactor

Assume there exists a car rental system, where any customer can rent a car. For each rental a car will be assigned from the available inventory and a price quote engine would calculate price.

Now the head of sales comes up with a new requirement to offer prices based on customer start date. So, this legacy car rental application has to be updated to accommodate his request.

Identify the change point

A point at which the change has to be made needs to be identified first. Based on the architecture above, to support the new pricing feature the change has to be made to the PriceQuote. In order ensure price quotes based on customer profile, PriceQuote component should also be aware of the Customer object.

Find the inflection point

The inflection point is in the call tree where any change below it will either be evident or insignificant. The test point is often not the change point.The inflection point for our change is at the Customer component. Customer component has to effectively tested in order to ensure the price quote works right for different types of customers.

Break Dependencies

One of the challenges on writing tests are mocking objects. It becomes messy if the test object depends on few other objects for instantiation. Then those classes should also be instantiated.

Укрощая зверя: legacy-код, тесты и вы

Legacy-код — это «старый» код, возраст которого может быть как 2 месяца, так и 10 лет. Часто его писали разработчики, о которых в компании смутно помнят. Возможно, их вообще не было, а legacy-код родился вместе со Вселенной во время Большого Взрыва. С тех пор требования к нему менялись много раз, код правили в режиме «нужно было еще вчера», а документацию никто не писал, как и тесты. Legacy-код запутан и хрупок, в нем не видно ни начала, ни конца. Как к нему подступиться?


Здесь и далее кадры из сериала «Рик и Морти». Авторы Джастин Ройланд и Дэн Хармон.

Подбираться к нему нужно с тестов, но готовьтесь к боли. Проблемы начнутся уже с того момента, как вы решите взяться за такой проект. Вам нужно понять, зачем вы хотите за него браться, убедить руководство одобрить тестирование legacy-кода, а коллег — помочь. После этого возникнут вопросы, с чего начать изучение, какие тесты запустить первыми и как все не сломать? Но главное — как не впасть в отчаяние, когда поймете, что работе нет конца.

Кирилл Борисов 12 лет в индустрии, за эти годы прошел долгий путь по костылям, битому коду и гниющим каркасам старых систем: от монолитных учетных систем до микросервисов авторизации. Путешествие наградило его опытом и историями, которыми он поделится в виде ценных советов.

У меня есть мечта — когда-нибудь поработать над новым проектом. В нем все будет хорошо с самого начала и свежо, как первый снег: тесты, архитектура и смысл. Но это лишь мечта, потому что уже 10 лет я продаю свой талант за деньги и перехожу из одного legacy-проекта к другому.

За это время у меня не осталось нервов, но я могу сберечь ваши, поделившись своим опытом взаимодействия с legacy. Я расскажу, как укрощать зверя (legacy-код): работать с кодом и людьми, внедрять тестирование, нужно ли это делать и как к этому относятся и разработчики.

Чего здесь не будет:

  • Советов, как писать тесты. Множество книг, статей и разных видео закрывают этот вопрос.
  • Обсуждения методологий. BDD, TDD, ATDD — все на ваше усмотрение.
  • Всего, что может нарушить NDA. У людей долгая память, а у юристов — длинные руки.

Что такое legacy-код

Определений много. Я считаю, что это «достаточно старый» код возрастом от 2 месяцев до 10 лет. Legacy-код запутан и хрупок, но как гигантский змей пожирает свой хвост.

Именно это не позволяет начать его спокойно тестировать. Все разработчики, от начинающих до опытных, когда приходят на legacy-проект, хватаются за копье тестов и несутся убивать это чудовище. Копье ломается, а вместе с ним люди. В итоге остается разработчик без признаков жизни, который работает на legacy-проекте десятки лет.

Возможно ли побороть этого зверя? Да, но нужна подготовка.

Подготовка

Борьба со зверем не так важна, как подготовительный этап. Он начинается с трех вопросов самому себе.

«Зачем я это делаю?» Серьёзно, зачем? Ведь варианта всего два.

  • Вы хотите славы, чтобы люди говорили, как вы великолепны, потому что внедрили тесты.
  • Чтобы облегчить жизнь себе, коллегам и компании.

«Знаю ли я, что делаю?» Если вы писали тесты, то знаете. Если нет, то прежде чем бросаться на монстра, овладейте азами: напишите 3-4 теста, покройте небольшую часть кода, набейте руку и почувствуйте силу.

«Есть ли у меня на это время?» Замечательнос благими порывами вмешиваться в код и улучшать его, работая на будущее. Но, возможно, на это нет времени, когда горит настоящее. Если так, то проекту нужны вы, а не светлый образ будущего.

Когда вы ответите на все вопросы утвердительно — переходите к следующему этапу.

Разведка местности

Изучите структуру проекта. У вас есть представление о структуре проекта, составных частях и принципе работы? Наверняка да, но, возможно, оно не совпадает с реальностью. Важно понимать, с чем придется столкнуться перед началом работы. Посвятите немного времени, чтобы пройтись по проекту и изучить его досконально.

Составьте схему зависимостей. Ни один проект не живет в вакууме. Базы данных, внешние сервисы, библиотеки — все это может использоваться в проекте.

Что сделано до вас? Возможно, вы не первый, кто боролся со зверем. Изучите наработки «предков», которые сгорели и ушли с проекта.

После разведки переходим к боевым действиям.

Борьба с организацией

Первый раунд — борьба с вашей организацией. Главный в ней — ваш менеджер, непосредственный начальник.

Менеджер. Он не так страшен, как кажется. Это обычный человек с обычными потребностями: сдать проект вовремя и без лишних проблем, получить за это деньги и бонусы и жить дальше.

Руководитель не против ваших начинаний. Он против того, чтобы вы кидались на проект с криками: «Тесты! Тесты! Тесты!». Если будете так делать, он посмотрит на вас как на человека, который тратит его время и тормозит остальных.

Покажите пользу. Менеджер говорит на языке пользы, времени и денег. Поймите, что им движет желание закрыть проект в срок и получить больше результата за меньшие ресурсы.

Тест не стоит подавать так:

— О, это будет круто!

Свои идеи надо продвигать так:

— В прошлом квартале у нас было 50 падений, которые можно было исправить на стадии разработки продукта. Исправить можно с помощью тестов. Они подтвердят, что внесённые изменения не изменили функционал, если мы того не ожидаем. Мы сэкономим часы, потраченные на устранение этих проблем и снизим сумму неустойки, которую выплатили из-за неработающей системы.

Произнося «оптимизация, деньги, экономия времени», вы говорите на языке менеджера. Когда он слышит эти слова, то проникается идеей. Он видит в вас не очередного оголтелого программиста, увлеченного свежей технологией, а человека, который заинтересован в улучшении продукта. Все ваши идеи он не одобрит сразу, но высока вероятность, что предложит сделать Proof Of Concept.

Proof of Concept повышает шансы. Предоставьте менеджеру отдельный изолированный участок кода, подсистему, которая покрывается тестами, запускается и работает. Это можно сделать, если взять один из наболевших багов, который всплывает с определенной периодичностью и попытаться его отловить и устранить тестом. PoC подтвердит ваши намерения, покажет, что у вас есть план и ваша работа приносит результат.

Не обещайте много. Для менеджера важны цифры: какие результаты, сроки и какими силами. Но менеджер — существо жадное до результатов. Не обещайте слишком много с самого начала. Если пообещаете решить все проблемы сразу, менеджер пойдет с этим к начальству. Начальство скажет: «Замечательно!», но сократит финансирование и срежет сроки в надежде, что мы сдадим систему намного раньше.

Когда договоримся с менеджером, переходим к тем, с кем приходится работать каждый день.

Коллеги

Не любят перемены. Типичный коллега на типичном legacy-проекте — это человек, который потерял веру в жизнь и будущее. Он не склонен к изменениям и смирился с судьбой: «Я здесь навсегда, выхода из болота нет». Проблема в том, что вы начинаете мутить воду в этом болоте. Вы требуете, чтобы он писал и запускал какие-то тесты, а он хочет выполнить свою работы, закрыть задачу и уйти домой.

Заинтересуйте коллег пользой — объясните, почему им станет лучше. Например, они постоянно тратят время и силы, оставаясь после работы, чтобы залечить какие-то баги. Надавите на это: «Если не деплоить на продакшн сломанный код, не придется тратить время на его починку. Напишем тесты, будем вылавливать такой код, меньше будет ломаться».

Проявите терпение и эмпатию. Вы общаетесь с людьми — спросите почему их беспокоит ваша идея? Предложите найти точку соприкосновения, чтобы понять друг друга. В этом основная тактика работы с людьми: не враждуйте, не сталкивайтесь лбами, будьте дружелюбнее.

Вам может помешать презентация идеи перед собранием коллег на очередном стендапе команды. В коллективе работает механизм «группового мышления»: никто не хочет принимать решение, все смотрят друг на друга и видят, что никто не горит энтузиазмом.

Для решения этой проблемы есть один грязный трюк. К сожалению, в своей жизни я пользовался им не раз.

Разделяйте и властвуйте. Подойдите к одному из коллег за обедом или в уголке и скажите: «Вся команда уже подписалась, ты один тормозишь процесс. Может быть, мы найдем общий язык?»

Пройдя всех по очереди вы подпишите всех. Всем будет стыдно признаться, что они подумали, что все остальные уже подписались. Это бесчестно и ужасно, но работает. Используйте этот прием ответственно и в крайнем случае. Помните — вам еще работать с этими людьми.

Когда разобрались с коллегами, нас ждет еще один жадный зверь.

Борьба с машиной

Это хитросплетение кода, которое называется продуктом. Начнем с азов.

Разберите хлам. Тестировать необходимо так, чтобы при минимальном воздействии на систему получать проверяемый результат. Но любая legacy-система полна данными: они добавлялись годами с момента запуска и влияют на поведение системы. Поэтому необходимо тестировать «с чистого листа».

Подготовьте «сферическую систему в вакууме»: опустошите источники данных, сделайте минимальные конфиги, которые запускает система, отключите все возможные «хаки» и «фичи». Заставьте систему запуститься. Если запустится — у вас есть минимальный набор данных, который необходим для функционирования. Это уже хорошая отправная точка — «чистый лист».

Применяя какие-то измеряемые воздействия, например, нажатие на определенную кнопку, вы получите измеряемый рабочий результат. С этим можно переходить к следующему шагу.

Распутайте данные. Любой legacy-проект работает на принципе «надо сдать вчера». Все, что вы проходили в университете или читали в книгах, здесь не работает. Когда начнете тестировать, столкнетесь, например, с циклической зависимостью, невозможной для воссоздания в программе, но необходимой для функционирования.

Начните с «главного объекта». Чтобы разобраться с лесом зависимостей, попробуйте задуматься о том, какой объект главный. Например, для системы учета склада главный объект — «ящик». С ним связан объект «полка», а с «полкой» — объект «ряд».

Воссоздайте необходимый минимум. Если смотреть по ссылкам между объектами и переходить всё глубже по дереву зависимостей, вы сможете определить необходимый минимум данных зависимых объектов. Вам его нужно воссоздать, чтобы система работала и могла функционировать для тестирования вашего функционала.

Не бойтесь менять ссылки. Возможно, придется засучить рукава и погрузиться глубоко в это месиво: удалять и менять ссылки, изменять структуру базы данных. Вы пришли, чтобы улучшить систему, поэтому не бойтесь вводить изменения.

Переходим к тестированию. Для запутанных старых продуктов хорошая стратегия — это smoke-тесты.

Smoke-тесты

Понятие «дымовое тестирование» пришло к нам из мира электроники. Один инженер собрал гигантскую схему с кучей лампочек и проводов. Но прежде, чем начал тестировать, просто включил схему в розетку. Если пошел дым, значит что-то пошло не так.

В информационных системах концепция smoke-тестов достаточно простая. Представим веб-сервис, у него есть endpoint. Попробуем отправить ему GET-запрос без параметров. Если по какой-то причине продукт неожиданно сломался (ошибка 500), то что-то пошло не так.

Smoke-тест — хорошее начало. Это тест, который проверяет некоторую функциональность и дает понять, что система работает или сломана. Даже простой запрос к самому простому endpoint уже затрагивает больше 1% кода. Такими небольшими тестами готовим плацдарм для дальнейшего тестирования.

Smoke-тест вскрывает множество проблем. Возможно, что за все время функционирования сервиса никто не догадался отправить запрос без параметров.

Используйте такую тактику, чтобы покрыть несколько основных точек входа в вашу программу: форму ввода логина/пароля, основные веб-сервисы, кнопки. Это что-то уже можно показать менеджеру и коллегам.

Функциональные тесты

Это не тесты отдельных классов или метод, а самый высокий возможный уровень тестирования определенной части функционала.

Представим функционал «сгенерировать отчет в сервисе». Вместо проверки отдельных частей, тестируем ситуацию запроса на создание отчета с определенными параметрами и получим файл с данными. Не обязательно знать механизм генерации отчета, но если с определенными входными данными сервис выдает определённые выходные данные, то этот черный ящик с некоторой вероятностью работает как надо.

Покрытие основного функционала подобными тестами позволяет быстро стартовать и сразу покрывает большие участки. Вы будете уверены, что код работает хотя бы приблизительно так, как вы представляете, приобретете больше уверенности, набьете руку и вскроете еще больше проблем.

На иглу функциональных тестов легко подсесть: «Я же тестирую реальный функционал! Это то, с чем сталкиваются пользователи».

Функциональный тест задействует большие куски кода, которые могут взаимодействовать с гигантскими объемами данных. Поэтому 3-4 функциональных теста — это хорошо, 10 хуже, а тысячи тестов, проходящие 9 часов, — перебор. К сожалению, такое тоже бывает.

После функциональных тестов беритесь за unit-тесты. Но о них я не буду рассказывать — вы и так все знаете.

Мы прошлись по азам машинного тестирования и возвращаемся к основной теме. Коллеги и менеджер — не самый страшный враг в бою с legacy. Самый страшный враг — вы сами.

Борьба с собой

Будьте готовы к тому, что путь будет казаться бесконечным. Работа на неделю в вашем плане займет полгода без перспектив завершения проекта.

Сопротивление неизбежно. Все союзники со временем начнут сомневаться, пытаться сойти с колеи, уговаривать бросить тесты и перейти к фичам. Будьте к этому готовы. Напомните всем, зачем вы вообще во все это ввязались, сколько сил и времени вложено. Слабый аргумент, но может сработать.

Никто не гарантирует успех. Даже если проявите героические усилия, вложите всего себя в работу, ваш проект все равно может сгореть, а крестовый поход с тестированием завершится ничем.

Это нормально, это не конец жизни и карьеры. Это даже не подтверждение того, что вы плохой профессионал. Единственный вывод здесь, что конкретно этот проект завершился неудачей.

Зато у вас появился опыт и знания. В следующий раз, когда возьмете в руку новое копье, и ваш конь разгонится на очередную ветряную мельницу, вы будете готовы сломать и это копье, но позже, другим методом и с меньшим ущербом.

Теперь обидное, горькое и вечное.

Напутствия

Не бойтесь обратной связи. Мне приходилось наступать в эту ловушку и видеть, как в нее попадают другие. Я что-то сделал и принес похвалиться коллегам: «Я сделяль!» Но неожиданно оказывается, что мой удобный механизм неудобен коллегам, а я и не спрашивал.

Пишите тесты, пробуйте то, что внедряете. Часто внедрение нового тестового фреймворка увлекает, а непосредственно сами тесты вы не пишите. Тогда может случиться так, что как только их напишите, поймете, что не сможете воспользоваться тестами. Возможно, коллеги тоже это видят, но молчат, либо просто не пишут тесты.

Помогайте коллегам с проблемами, даже если они об этом не просят. Помощь не означает взять всю работу на себя — это расслабляет коллег и снимает с них ответственность, а «автобусное» число снижается до единицы.Тогда вы становитесь человеком-тестировщиком: что-то сломалось, CI красный, тест-гайд. Помогайте в рамках разумного.

«Автобусное» число не шутка. Вы не сможете всегда тащить проект на себе. Каждый может выгореть, уйти в отпуск или уволиться. Поэтому передавайте коллегам ваши знания и ответственность, которая необходима, чтобы справиться без вас. Это поможет избежать неприятных звонков, когда вы расслабились на пляже, а CI снова красный.

Улучшайте механизмы тестирования. Многих проблем можно избежать просто потому, что медленные тесты неожиданно стали быстрыми. Раньше они занимали 20 строк кода, а теперь одну. Вы этого не замечали, потому что один раз что-то написали и забыли: «Работает — не трогай!» Но это правило не всегда применимо.

Вы — не центр Вселенной. Снова повторю, что «автобусное» число это не шутка. Не раз сталкивался с ситуацией, когда человек начал тестирование, а потом получил предложение в проект посвежее: все бросил, убежал, а комментариев и документации не оставил. Все работает до нового коммита, а починить невозможно — никто не понимает, как все устроено.

Не хочу, чтобы вы оказались этим человеком. Не превращайтесь в ограничивающий фактор.

  • Пишите документацию.
  • Проводите тренинги.
  • Распространяйте свой опыт.

27 марта на Moscow Python Conf++ Кирилл расскажет о технической стороне рефакторинга кода с Python 2 на Python 3 — что может быть актуальнее в 2020 году.

Что еще нас ждёт на конференции, можно посмотреть в статье с обзором программы или в соцсетях (fb, vk, twitter) и telegram-канале мероприятия. Скоро увидимся!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *