Это сопроводительная статья к моей речи об искусстве отладки, которую я впервые дал на Fronteers в Амстердаме в 2015 году.
TL; DR: изучите все инструменты, которые доступны для использования, используйте их по мере необходимости, наслаждайтесь исправлением ошибок — конечно, веселее играть на клавиатуре и работать на 6-месячном диске.
Прежде чем мы начнем, хотя …
Как перейти к концу …
Не.
Напишите.
Ошибок.
Хотя…
Предполагая, что вы не робот, и, возможно, вы написали одну или две ошибки в свое время, правда в том, что серебряной пули нет.
И на самом деле, я солгал минуту назад, «не пиши ошибок» — полная противоположность обучению отладке. Тебе нужен опыт. Вы должны столкнуться с ошибками, чтобы понять, как к ним приблизиться.
Там нет жесткого и быстрого навыка, который вы можете приобрести для отладки (или, я так полагаю). Оно приобретается во времени, когда вы боретесь, когда впервые сталкиваетесь с чем-то. Тратить часы и часы, пытаясь решить проблему, но результат: вы не будете тратить часы и часы в следующий раз.
В компании, в которой я работал 10 лет назад, когда на работу пришли новые сотрудники, мы бы все их взволновали по поводу работы и тех проблем, которые мы решали, но в день их присоединения они были бы назначены на ошибки для 3 месяца. Это как бы не оправдало их ожиданий, но мы обнаружим, что через 3 месяца они будут просить об ошибках.
У них был шанс поиграть во многих сферах бизнеса, тогда как другие разработчики кодировали одну и ту же функцию в течение 3, 6 и 12 месяцев, и даже тогда она заработала бы, имела бы ошибки, и наш ловец ошибок включился бы, исправить ошибку и получить кусочек славы.
Я полагаю, что получение практического опыта является ключом к тому, чтобы быть хорошим разработчиком, но на самом деле знать, как что-то отлаживать. Дизайнер из этой компании 10 лет назад Крис — он был CSS-мастером. Он знал все ответы, когда разработчики на стороне сервера застряли бы на простых вещах. Я часто спрашивала его, почему макет сломался так легко, что я считал относительно простым дизайном. Его ответ, часто тихий, был «добавить zoom: 1
к этому элементу».
Он завершил этап отладки в голове и пришел к разумному предложению, что это конкретное изменение может исправить мою проблему, почти полностью, потому что он видел так много изменений визуальной ошибки, которые он мог распознать на глаз.
Это то, что я буду делать во многих случаях ошибок, с которыми я сталкиваюсь. Я буду знать конкретную систему достаточно хорошо, чтобы позволить себе приступить к решению.
Но прежде чем я продолжу, у меня есть два отказа от ответственности …
Отказ от ответственности # 1 — Рамки
Прежде чем кто-либо станет проповедовать, это не лучший способ отладки в сети. Есть много способов . Это просто то, что я знаю, и как я это делаю. Если это поможет тебе, супер. Если вы делаете вещи по-другому, это тоже круто.
Я лично не использую фреймворки и большие (самоуверенные) библиотеки. Ember, Polymer, React, Anglular и т. Д. Я ими не пользуюсь. Я не нуждался в них во всем, что я сделал, поэтому у меня не было требования учиться (и, пожалуйста, не принимайте это как приглашение, чтобы научить меня!).
Это означает, что конкретные инструменты, которые я использую, могут быть неприменимы к вашему рабочему процессу. На самом деле вполне возможно, что он не совместим с вашим рабочим процессом.
Эта проблема частично связана со сложностью приложений, которые вы используете (под «приложениями» я подразумеваю вспомогательный код для загрузки сайта, который вы пытаетесь создать). Например, React создал свой собственный язык, чтобы дать разработчикам максимум
влияние на создание приложений , но из-за этого код, который он передает, совершенно бесполезен для людей и предназначен только для компьютеров / браузеров. Поэтому для его отладки требуется как минимум исходные карты, но поскольку (я могу только предположить) он также имеет свое собственное управление состоянием (и другие причудливые игрушки), вам рекомендуется установить расширение devtools для отладки ваших приложений React (я полагаю, что Ember здесь тоже похоже).
путаница
Это не значит, что эта информация для вас бесполезна, я коснусь идей, которые важны при отладке, я просто говорю: я не использую фреймворки, поэтому я тоже не отлаживаю с ними.
Отказ от ответственности № 2 — я редко кросс-браузерный тест
Ага. Я сказал это. Но прежде чем бросить меня в волков, выслушай меня. Я не проверяю браузер, потому что чаще всего моя работа требует написания JavaScript. Ванильный JavaScript, а не JavaScript, взаимодействующий с DOM.
На мой взгляд, есть два типа JavaScript, которые меня интересуют: взаимодействие с браузером и все остальное.
Все остальное должно работать в ES5 (возможно, с добавленными битами ES6), но это все. Если я не поддерживаю IE8 (какими последние проекты я не являюсь), весь мой JavaScript будет работать во всех браузерах, потому что это что-то вроде:
function magicNumber(a, b) {
return Math.pow(a, b) * b / Math.PI;
}
Неважно, где выполняется код выше, если есть ошибка, есть ошибка во всех браузерах. Если ошибки нет, ошибки нет, и все.
Также это не означает, что мой код не тестируется в других браузерах. Если это возможно и необходимо, я буду запускать автоматические тесты в разных браузерных средах (используя такие инструменты, как Karma или Zuul, но вся автоматизированная система кросс-браузерного тестирования еще не исправлена, сейчас это своего рода беспорядок).
Опять же, это полностью связано с характером моей работы. Я расскажу позже, как (или даже буду ли) я буду проходить перекрестный тест
Искусство отладки
Это то, что я открываю во всех своих мастер-классах по отладке, и посмотрите, даже Википедия говорит, что это искусство, так что это вещь, хорошо!
Я разбиваю (в моей голове) отладку так:
- Копировать перевод: увидеть ошибку
- Изолировать перевод: понять ошибку
- Устранить перевод: исправить ошибку
Повторные
Воспроизведение ошибки — самая трудная часть всей работы. Чаще всего вы получаете сообщение об ошибке в виде:
Сохранение не работает
…вот и все.
Правильно, поэтому я не только должен быть дипломатичным в вашем ответе, сколько бы я ни хотел ответить просто «да, это так» [закрыть], но мне также нужно собрать как можно больше информации, чтобы иметь возможность повторить то, что этот пользователь видит.
Если сайт, о котором они говорят, это jsbin, то я знаю, что сохранение работает, потому что я просто использовал его, это просто означает, что сохранение работает для них (и, возможно, для других). Перевод: экономия может просто работать на меня .
Если я захожу на URL-адрес, о котором говорит пользователь, то он сразу прерывается. Это счастливый случай. Это лакмусовая бумажка, и ее всегда стоит делать. Никогда не прыгайте прямо в попытке повторить 100% — делайте это шаг за шагом. Тем не менее, более вероятно, что перед появлением ошибки произошел ряд событий, и мне нужно понять, что это было, а затем повторить их самому.
Осторожно, дотошно и систематически. Это важно, потому что я не буду делать это один раз, я должен иметь возможность делать это снова и снова (или, по крайней мере, дважды).
Есть несколько ключевых инструментов, которые помогают мне реплицировать среды, или, по крайней мере, два помогают определить, какие части среды я могу исключить:
Режим просмотра конфиденциальности / Инкогнито
Режим инкогнито в Chrome (и известный под другими именами в других браузерах) позволяет мне запускать сайт без (большей части) моих расширений браузера. Это также начнется с чистого листа в отношении файлов cookie, автономного хранилища и любой другой предварительно запеченной конфигурации, с которой идут мои «обычные» сеансы просмотра.
Я могу с уверенностью сказать, что я получаю по крайней мере одну ошибку, регистрируемую каждый год, что обычно очень странно и сводится к расширению румян в браузере пользователя, которое мешает коду веб-сайта.
Запустив в режиме инкогнито и не увидев ошибки, затем попросив пользователя повторить ту же задачу, я сразу же могу подтвердить, что в игре находится внешняя сущность (то есть, как правило, расширение).
Несколько профилей
В Chrome у меня есть личный профиль. Тот, который позволяет мне посещать мою электронную почту, не всегда прося меня войти (хотя … возможно, это плохо, но быстро продвигается).
У меня также есть два других профиля:
- Аноним — этот пользователь абсолютно чистый, без расширений, без истории
- Тролль — этот пользователь будет похож на Anonymous, но у него также будут отключены файлы cookie и максимальные настройки безопасности
Мне не часто приходится переключаться на эти профили (в основном потому, что я могу повторить ошибки ранее в моем тестировании), но они легко доступны.
Пользователь тролля особенно полезен, потому что очень легко (для меня) забыть, что некоторые пользователи имеют более высокие настройки безопасности, и в результате API, такие как, localStorage
будут генерировать исключения — которые, если они не обработаны, могут вызвать хаос.
Теперь, когда я могу последовательно копировать, пришло время как можно больше убрать, чтобы уменьшить шум и потенциальную путаницу, прежде чем я смогу исправить.
Изолировать
Изоляция — это устранение ошибки, насколько я могу. Если расширение является причиной ошибки, давайте отключим одно расширение за раз, пока не будет найдено плохое.
Если это ошибка в относительно сложном наборе JavaScript, требующем большого взаимодействия с пользователем, я задам себе вопрос: могу ли я выполнить рефакторинг этой конкретной области кода , чтобы я мог протестировать ее изолированно и внедрить предварительно запеченное состояние ?
Я создал jsbin.com именно для этой проблемы. Чтобы решить проблему, разденьте ее, а затем исправьте и поделитесь с кем угодно.
Как только он урезается настолько, насколько я хочу, я приступаю к исправлению ошибки.
Устранить
Это на самом деле легко, если заботиться о репликации. В эти дни (2015) я с большей вероятностью на самом деле создаю неудачный тест в моем проекте, который будет повторять ошибку, над которой я работаю, и затем я исправлю ее. Преимущества должны быть очевидны.
Это действительно довольно просто в этом состоянии. Точно так же, как и сам процесс написания кода (вы можете коснуться шрифта). Сложная часть заключается в решении проблемы, которая не происходит при нажатии клавиш на клавиатуре.
Когда вы не можете повторить …
Ну … тебе дерьмо не повезло, и ты можешь слепо кодировать решение, но это не отладка. Вам нужно подумать, есть ли у вас Heisenbug (да, мне нравится это слово!). Это ошибка, которая буквально меняет форму и форму, когда вы пытаетесь ее опросить.
Я лично столкнулся с некоторыми из них сам. Худшие виды (для меня) — это когда ошибки появляются только в моей системе CI (например, Travis). Ошибка, над которой я работал, была исправлена в моей локальной среде, и я достаточно хорошо понимал код, чтобы знать, что ошибка была исправлена, но мои тесты не прошли. Задача теперь другая, задачей была отладка тестовой среды, которая является CI закрытой системой.
Другое существенное время, когда я столкнулся с такой проблемой, было еще при использовании Firebug (который прекратился около 2009-2010 гг.). Firebug является / был навязчивым инструментом отладки, который вставлял контент в DOM для достижения отладки. Он также имел ошибку (как и devtools и все другие отладчики — см. Начало этого поста!). Это означало, что существуют определенные крайние случаи, в которые вы можете столкнуться, что вызовет ошибки в отладчике, делая отладку дополнительной … сложной.
То же рода верно и сегодня. Рекомендации по отладке с помощью временной шкалы devtools заключаются в том, что вы не включаете все флажки записи и в идеале закрываете все другие вкладки и все остальное, что может использовать WebKit (например, Spotify … Я предполагаю, что существует некоторая пересекающаяся ОС получить доступ к WebKit и Blink …). Это потому, что все это повлияет на производительность записи.
Подходы отладки
Доступные инструменты делятся на две категории:
- Наизнанку
- Снаружи
Я признаю, что это не хорошие имена. Под наизнанкой я имею в виду, что источник ошибки известен. Обычно можно добавить определенную функцию или строку кода, а также debugger
оператор, точку останова или условную точку останова (прерывание, когда выражение верно).
Снаружи интереснее то, что вы можете визуально определить, что есть ошибка, возможно, элемент ведет себя не так, как вы ожидаете. Растет число инструментов, помогающих отвлечь вас от визуальной проблемы и проникнуть в исходный код проблемы, не зная при этом исходного кода.
Эти инструменты включают в себя:
- Точки останова DOM — разрыв на модификации поддерева, модификации атрибута или удалении узла
- Ajax breakpoints — break when an XHR call is executed
- Replaying XHR — allowing you to re-inject the response from the XHR call
- Timeline screenshots — both against the network (usually boot time) and on the timeline during runtime
My Favorite/Most Used Tools
Finally I want to share with you some of the workflow I use and some of the tools that I always find myself returning to.
Workspaces & Real-time Updates
With devtools open, and the sources panel selected, simply drag the local directory you want to create a workspace for onto the source panel, and devtools will ask for access which you will need to confirm.
This doesn’t complete the step though. To let devtools know that a particular origin, like http://localhost:8000
is being served from your new workspace, you need to map at least one file. Right click a file from the origin list, and select «Map to file system resource,» and select the local file it relates to.
Now whenever you make any changes, you will be able to save and it will save directly to disk. Why is this important? Now you can debug and commit directly to disk without switching contexts, without switch from your editor to your browser.
What is also really fun and powerful, is that if the CSS files were also mapped, any changes in the elements panel to styles, directly update the CSS file attached. This means I can make really tiny visual changes in the elements panel (where I’m used to making changes) and it’ll already be saved for me to disk.
Undo
I’ve run a number of debugging workshops and the one consistent question that comes up after I show off workspaces is:
How do I revert the changes I’ve made in the elements panel
It would seem that developers are consistently much more fast-and-loose with the elements panel than compared with editing source code directly.
It’s still a fair question. To that I reply:
- Source control!
- Undo
Chrome devtools has really good undo support. I can make a whole series of CSS changes, and then move on to JavaScript and then make changes to the DOM, and I can still go back and undo all the CSS changes I made.
I’ve noticed that I do have to be focused on the particular panel and source for the undo to work (which I suppose the undo history is associated with the panel), but it’s really good.
Obviously when you reload, you lose the history. It’s the same with Sublime Editor, if I unload and reload Sublime (i.e. restart the application) I’d expect the undo history to be lost.
Console Shortcuts
$
&$$
— akin to jQuery’s$
function to query elements on the page$_
— the result of the last expression$0
— the currently selected DOM node in the elements panelcopy(...)
— copy to the clipboard, and willJSON.stringify
objects, but also get the outer HTML of DOM nodes,copy($0)
is pretty common for me
Timeline Screenshots
Really nice way to go back in time to see what in the application’s boot (or interaction time) changed something on the page. I used this recently to fix two different problems.
The first was reviewing the boot up screenshots for jsbin.com, and seeing that the font was loading right at the end, but taking up a reasonable amount of time (WRT entire boot time). I could see this because the font would flash into place right towards the end of the document being ready. I was then able to use font loading techniques to make the font load via local storage and improved the perceived boot up time.
The second time was with my product confwall.com. The problem was that there was significant latency in loading the tabbing system. If you watch the animation below (running at 50% speed) you’ll see the tabs are slow to render:
This is also captured in the rendering timeline via the «camera» icon:
From this, I could move the point in time where the tabs finally re-rendered into the right layout, and work backwards to find what was running and blocking.
Throttling
Throttling the network gives me a really quick view on emulating slow or entirely offline connections to get an instant view on the effect of a slower network.
A typical example is: what does my site with custom fonts look like over a slow connection? Is it blank for a long time? Are other assets holding up the font rendering? Is there anything I can do about it?
Network Detail & Reply
The visualization of the network requests is useful, but I also find that inspecting headers and copying the raw response is extremely useful.
I’ve also found that when I’m debugging a server side bug where the response is incorrect (like sending HTML back instead of JSON), I can debug, fix and restart the server, and instead of refreshing my browser and blowing away the state and current stack — I can simply «Replay XHR» and my code will re-run the request, and (IIRC) the callback will fire with the updated server content.
Break on DOM Changes
As I mentioned earlier, «break on DOM changes» is one way which I will debug from an outside in approach. I’ve used this plenty of times when I know there’s a visual change, but I’m unsure what the source of that change is.
I do find it tricky to know exactly which «break on…» to use. Usually «break on attribute modification» is simple — i.e. if the className
changes, the code will break. Otherwise, I tend to just select everything until the code breaks, and then I’ll either step through or step backwards through the call stack.
An extra protip here is sometimes the call stack will be decapitated due to an async call. Devtools offers a feature (which is expensive on memory, so remember to turn it off) on the sources panel. Check the «Async» box and repeat the bug. You’ll now have the full call stack across the asynchronous calls.
Surface Scans for Memory Leaks
Finally, memory leaks are traditionally (for me certainly) the hardest part of debugging. In truth, I’ll rarely look at memory unless I feel there’s something jumping out at me. However, devtools has really advanced in the ease required to dig around for leaks.
There’s two approaches I will take, fully informed by this excellent Chrome video from a few years ago:
- Surface tests looking at the staircase
- Using the profiling tools to capture clues to the source of the leak
The staircase effect is the first initial clue as to whether you have a memory leak. For me, the trick is to reliably reproduce the leaking effect. I’ll personally start a timeline recording with «Memory» selected (and nothing else). I’ll start the interaction, and before stopping, click the dustbin which forces a garbage collection, and then I’ll repeat the process again, and then end the recording.
What I’m trying to do here is: establish the baseline memory use (the data before I start the interaction), run an interaction. If there’s a significant amount of memory that couldn’t be garbage collected, then I have a leak. Then onwards to profiling.
Profiling can take two approaches. The first is to capture two heap dumps, one at the start of the interaction, and one at the end. I might also run two interactions, but before I start the second run, I’ll force a garbage collection. The task is then to compare the deltas. I’ll select the second heap dump, and change it from a «summary» to «comparison» and order by «deltas.» Now I’m looking for is red items in memory. These are items that couldn’t be garbage collected.
This will then (hopefully) yield clues as to what is leaking. Usually DOM nodes, and what JavaScript references are still pointing to the nodes. Frustratingly it’s usually inside a JavaScript library, so some knowledge of how libraries work helps a great deal.
Wrap Up
As I said at the start, there’s no silver bullet. I suspect many readers of this post will have skimmed right to the actionable parts and copy & pasted. Which is cool, I’d do the same.
Honing your debugging skills is a long game, directly linked to writing code which leads to the artefacts of bugs. Hopefully you’ll jump at the chance of debugging too!
Remember, it’s also worth taking a break from debugging too, many, many bugs have been solved without being near computers (long walks, showers, etc) — because computers can be a bit stressful sometimes too…!