Как пропущенный var сорвал наш запуск

Original author: Geoff Hayes
  • Translation
Перевод этой заметки никак не нацелен оттолкнуть читателя от использования Node.js, и на старуху бывает проруха, а лишь призывает быть внимательными, и, возможно, подскажет решение тем, кто вдруг столкнётся с подобным поведением своего приложения. Лексика автора оставлена без особых изменений и цензуры.

Кратко сказка сказывается, да долго дело делается, MelonCard сегодня был представлен на TechCrunch вместе с другими компаниями, как вдруг всё внезапно сломалось. Каждая. маленькая. мелочь. Мы только что обновили сайт, чтобы он выглядел и ощущался более отзывчивым, используя long-polling NodeJS, с крутейшим динамическим фронтендом на jQuery Templates и KnockoutJS. Приложили все усилия и провели ручное и юнит-тестирование с помощью Vows. Все системы готовы, полный вперёд и всё такое? Не тут-то было.

Наша система на NodeJS использует состояние пользователя, например «Я ожидаю обновления этих двух записей», а сервер (отталкиваясь от проверки временной засечки) возвращает либо «Ваши записи актуальны», либо «Запись xxx изменилась на yyy» (на самом деле всё немного сложнее с разделёнными переменными Redis, сессиями и прочими проверками безопасности для взаимосвязи Rails, MySQL, Redis и Node). Всё вот так кристально просто, но даже простой код NodeJS может обернуться адом, когда что-то идёт не так. Это случилось сегодня.

После того, как статьи о нас были опубликованы, к нам хлынул поток обрадованных пользователей (скажем, 50-100 новых пользователей в час). И внезапно всё развалилось. Страницы больше не работали; наши почтовые ящики начали набиваться сообщениями от недовольных пользователей. Я налил кофе и приготовился к битве.

Моей первой мыслью было то, что NodeJS отлично держит нагрузку, он этим и славен. Пятьдесят или сто пользователей никак не могли угробить систему. И как выяснилось это была не вина NodeJS, как такового, но об этом позже. Сервер начал возвращать соврешенно неожиданные ответы, как если пользователь говорил «У меня есть записи a, b и c», а сервер отвечал «Ты идиот, сотри записи x, y и z, а вот тебе записи a, b и c». Сфокусироваться и воспроизвести проблему было невозможно, учитывая ужасную обработку ошибок и возможности дебага в Node. Следующей командой приходилось постоянно пользоваться (да, прямо на продакшне):

NODE_ENV=’production’ node/privacy.js | grep “Возвращаемые результаты”

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

Trace: at EventEmitter. (/—/node/privacy.js:118:11) at EventEmitter.emit (events.js:81:20)

Строка, упоминающаясе здесь (единственная, о которой сообщал Node):

process.on(‘uncaughtException’, function (err) { console.log([‘Caught exception’, err]); console.trace(); });

Ну, хотя бы приложение не валилось, но всё равно — отталкиваться было не от чего. Хочется отметить, что ничего из того, что мы делали (ручное тестирование интерфейса, юнит-тестирование, обработка ошибок и т.п.) не выявляло ошибок, связанных с этой строкой. Да, нужно было бы использовать нагрузочное тестирование, но нет уверенности, что оно бы выявило это злоключение.
После четырёх часов дебага (и перевода заглавной страницы в 503 — Временно Недоступно), и пока мой соучередитель персонально отвечал каждому расстроенному и любознательному пользователю извинениями, я заметил, что пользователь путал параметры моего запроса с параметрами запроса других пользователей. Прямо скажу, сервер был спроектирован так и работал так, чтобы возвращать только ВАШУ информацию по ВАШЕМУ запросу, но он путал то, что ВЫ запрашивали. То есть, вы спрашивали «Люблю яблоки и дыни», а он отвечал «Ерунда, вы любите манго». То есть всё было безопасно, но всё равно чертовски неправильно. С чего бы вдруг мой ExpressJS сервер путал то, что я его спрашивал. Я начал копать и нашёл вот что:

app.all(‘/apps/:user_id/status’, function(req, res, next) {
// …
initial = extractVariables(req.body);
});

Выглядит плоховато? Да это просто провал. Я не эксперт в JavaScript'е, но попробую объяснить как умею. В JavaScript'е объявления переменных происходят либо в контексте функции, либо в глобальном контексте (с некоторыми замороками прохождения через вложенность контекстов от текущего до глобального). Когда я создал «initial» без «var», был совершён проход от текущего контекста и быстренько попало в глобальный, и создало там глобальную переменную «initial». Когда пришёл следующий запрос, такой же проход был совершён ещё раз и записал данные в ту же самую переменную (ту самую, которую предыдущий запрос всё ещё собирался использовать). И так происходило с каждым следующим запросом. Когда сервер отвечал на запросы после некоторой обработки, он читал из этой постоянно обновляемой переменной и возвращал бредовые результаты. Полное дерьмо. Нужно было всего-то написать так:

var initial = extractVariables(req.body);

Такой код бы создавал переменную в контексте моей анонимной функции, и не было бы никакой возможности перезаписать ещё другим запросом. Это было любительской ошибкой, но мимо неё прошли, не заметив, все дебаги и тестирования, которые я только смог применить.

Вот и настал момент, когда вы должны сказать «нужно было использовать CoffeeScript». И вы окажетесь правы. В других обстоятельствах всё могло бы быть ещё хуже (что бы было, если бы я ошибся контекстом для переменной сессии?). Ко всему прочему недостаток нормальной обработки ошибок (в Rails мы ловим ошибки по стектрейсам и отправляем уникальные стектрейсы команде через электронную почту), и какого-либо нормального средства для дебага (кроме grep и less), унесли меня в годы, когда программирование не было приятной вещью. Или, возможно, мне просто нужно было быть внимательнее.

После четырёх часов перерыва в обслуживании и нескольких сотен расстроенных пользователей, я нашёл проблему и быстро поправил на продуктивном сервере. Тучи рассеялись, птички защебетали и выглянуло солнышко. Мы начали отвечать пользователям извинениями, посчитали убытки и двинулись дальше. Но мне до сих пор нелегко от того, какой ущерб принесло одно отсутствующее ключевое слово. Неужто то, что я пропустил один var делает меня ущербным?
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 88

    +11
    Много раз встречал, что народ наплевательски относится к глобальному контексту и временные переменные утекают из разных мест.
    Я, лично, всегда проверяю, а не появилось ли чего нового там, чего не должно быть. Пока вручную, но т.к. уже несколько раз приходилось перелапачивать исходники (~400 js файлов), в поисках места с косяком, то планирую это дело автоматизировать.
      +4
      По-моему все IDE, с которыми я работал (Eclipse, IntelliJ IDEA, NetBeans), выводят предупреждение, если попытаться инициализировать переменную без var. Вообще было бы неплохо pre-commit хук поставить на контроль версий, чтобы не давало даже такую ересь коммитить.
        0
        и что даст хука? а если используется переменная, определенная в вызывающем контексте и тот контекст может быть далеко не window…
          +1
          Intellij IDEA понимает scope и не ругается, когда используется переменная, определённая выше. И не нужен SoffeeScript.
          IDEA так же отличает глобальную область видимости от scope функций. Другими словами, если Вы напишите в файле first.js:
          var global = {}; // В глобальном контексте

          или так:
          window.global = {};

          То в файле second.js не будет предупреждений при обращении к этой переменной (если он в том же проекте).

          Либо JSLint, тут кому что больше нравится, инструментов хватает.
          0
          как минимум ide рисуют такие переменные другим цветом
          +3
          А синтаксический сахар типа CoffeeScript не пробовали? Он должен избавлять от многих бед. В Rails 3.1 используется по умолчанию.
          • UFO just landed and posted this here
          +3
          Enterprise, говорите?)
          Все это сильно напоминает вот такие ошибки.
            +22
            вам прекрасно помогло бы это:

            "use strict";
            


            и это:

            jslint.com/
              +1
              Автору оригинальной заметки наверняка бы помогло.
                +3
                Если кодить в WebStorm, то пропустить var практически невозможно. Переменные без var очень броско выделяются в коде: подчеркиваются голубым зигзагом, а их фон заливается примерно таким цветом #F0F0E7. Думаю, и другие редакторы такое умеют.
                • UFO just landed and posted this here
                    +1
                    А смысл?
                    Там и так очень четкая валидация всего чего можно.
                    А теперь что — WebStrorm will hurt your feelings?
                    • UFO just landed and posted this here
                        0
                        На самом деле, не всего. После webstorm'a jslint может найти ещё много интересных кусочков кода)
                  +6
                  Сейчас вам, автор, напишут, что программисты никогда не должны ошибаться. И вообще что вы — лох и посмели посягнуть на святыню (node.js). А сам JS — великолепный язык, на котором прекрасно пишутся крупные приложения, просто вы не умеете его готовить.
                    +3
                    Дак перевод же.
                      +2
                      Дык правда же. Пойду сейчас на ruby сделаю подобное и скажу, что Rails говно.
                      0
                      Вообще-то, JS — действительно великолепный язык. Его только подчистить от ошибок молодости надо (и этих ошибок не так много)
                        0
                        А можно огласить Ваш список? Хочу сверить со своим…
                          0
                          Исправить:
                          — По умолчанию глобальный контекст переменных
                          — Function-scope (хотелось бы block-scope)
                          — Semicolon insertion

                          Добавить:
                          — модульность

                          И уже можно пользоваться.

                          Что-то еще было, только ща котелок не варит уже :)
                            –9
                            Мой шире… Еще заменить прототипное ООП на классовое и выкосить неявное приведение типов, а с ним все приколы с "==".
                              +4
                              > Еще заменить прототипное ООП на классовое

                              А, ну я этого ожидал. почему-то всем в обязательном порядке хочется втянуть C++-style OOP за уши в любой язык.

                              Не нужно никакого «классового ООП» в JS.

                              > выкосить неявное приведение типов, а с ним все приколы с "==".

                              Да, согласен. Строгая типизация не помешала бы
                                –2
                                > Не нужно никакого «классового ООП» в JS.

                                Можно спорить долго, но факт остаётся фактом: JS — единственный популярный язык с ООП на прототипах, да и в нем в конечном итоге все ваяют классы поверх прототипов а дальше по старинке.
                                  +2
                                  > JS — единственный популярный язык с ООП на прототипах, да и в нем в конечном итоге все ваяют классы поверх прототипов а дальше по старинке.

                                  Поэтому не надл натягивать на язык то, для чего он не создавался, а обучать программистов действовать не по старинке ;)
                                    0
                                    И реально кого-то удалось обучить не действовать по старинке?
                                      +1
                                      Удавалось конечно. Какой это программист, если он не способен обучиться чему-то новому? ;)

                                      Более того, большинство современных фреймворков для JS, даже те, которые натягивают способы работы «по старинке» совсем не чураются использовать prototype-based OOP, и их внутренности очень интересны для его изучения.
                                        0
                                        Я имел ввиду научить с целью не писать под себя очередную реализацию ООП на основе прототипов.
                                          0
                                          Ну да :)
                                      –1
                                      > Поэтому не надл натягивать на язык то, для чего он не создавался

                                      Все так на полном серьёзе считают прототипы обдуманным решением. Интересно, если на начальную версию языка понадобилось 10 дней, то сколько времени было потрачено на предварительное обдумывание и проектирование?
                                        +4
                                        То, что на начальную версию языка пондобилось 10 дней, ничего не говорит о качестве обдумывания и проектирования — это раз. Вообще-то, ошибок в языке достаточно мало. Можно посмотреть, что об этом говорит сам автор языка, например.

                                        Во-вторых — JS — это, по сути, Self+Scheme с C-like синтаксисом и это очень хорошо. То, что вы не представляете себе другого ООП, кроме C++ного, не значит, что этот ООП надо натягивать на все языки ;)

                                        Ну и в третьих, Harmony добавит достаточно много вкусного сахара и попытается исправить некоторые из ошибок молодости.
                                          0
                                          > ничего не говорит о качестве обдумывания и проектирования — это раз

                                          … и тайком протащить туда его спасительные фишки: first class functions (замыкания сделал позже, но они были частью плана сразу) и прототипы (примерно как в языке Self).
                                            0
                                            И?
                                              0
                                              Цитата как бы намекает на широту и продолжительность обсуждения этого элемента дизайна языка Javascript.
                                                0
                                                Ни на что она не намекает. То, что автор пошел против желания руководства, никак не указывает на то, что он не думал перед тем, как вводить эту функциональность.

                                                В итоге JS — самый популярныя функциональный язык программирования в мире, но, как всегда
                                        –5
                                        Действительно, зачем создавать продукт здесь и сейчас, зачем думать о бизнес задачах, зачем захватывать не занятые пока еще рынки? Зачем думать о рынке труда и текущих знаниях большинства программистов? Нет! Лучше писать код, не поднимая головы, и не интересуясь ничем больше. И вместо основ экономики, например, выучивать как делать ООП на прототипах.
                                          +2
                                          Боже, какой поток демагогии.
                                        +1
                                        единственный популярный язык с ООП на прототипах


                                        Не единственный, на вскидку:

                                        * Ruby (появился раньше, чем js)
                                        * ActionScript 3 (близкий родственничек)

                                        У этих двух реализация наследования сделана на порядок удобнее (т.е. я бы сказал, она там есть из коробки). Это на мой взгляд, конечно. Всегда найдутся фанаты десятка различных сторонних реализаций наследования в чистом js (и ни одного стандартного способа в одну понятную всем строку, господа, ни одного!).
                                          0
                                          В Ruby не prototype-based ООП (емнип)

                                          ActionScript — это не близкий родственник, а младший брат, выросший из Javascript -> Ecmascript
                                            +2
                                            В Ruby очень интересная реализация, я привык ее считать прототипориентированной, но возможно, я ошибаюсь.

                                            В двух строчках описать сложно, но попробую:
                                            1) В руби нет понятия чисто-класс как шаблон (как в C++), в Руби все классы точно такие объекты, как и объекты.

                                            2) Method lookup использует цепочки «прототипов», очень приближенно говоря, у каждого объекта есть пару цепочек, указывающих на предков, к примеру цепочки class и superclass. Кратко можно глянуть в документации www.ruby-doc.org/core-1.9.3/Class.html#method-i-superclass

                                            Это позволяет расширять классы как угодно, во всех старых инстансах класса будут доступны новые методы. Чем не прототипы?

                                            3) Есть возможность клонировать объекты, а так же вы вольны:
                                            * создавать новые объекты, выдергивая уже готовые методы из существующих (Через Method#unbind)
                                            * создавать классы в рантайме, например используя некую функцию
                                            * расширять только конкретные объекты, не трогая остальные объекты этого класса.
                                            и т.д.
                                            * пройти всю цепочку предков и что-то сделать с каждым.

                                            Ну есть хочется вообще чего-то особо извращенного, типа смены class у созданного инстанса, то можно попробовать www.ruby-doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/Delegator.html#method-c-new

                                            это не близкий родственник, а младший брат, выросший из Javascript -> Ecmascript


                                            Ну младший брат это и есть близкий родственник, как я думаю :) Мне кажется, в AS3 есть интересные решения (смешать Java и JS и не взбалтывать).
                                              0
                                              Вот чуть более наглядная презентация по теме
                                              www.slideshare.net/burkelibbey/rubys-object-model-metaprogramming-and-other-magic
                                                +5
                                                То, что вы описываете у Ruby — это метаобъекты, а не прототипное наследование. В первом случае у нас есть чертежи объектов — метаклассы, и есть изготовленные по чертежам объекты. Во втором случае любой объект можно превратить в шаблон класса. Разница как между промышленной сборкой и размножением живых организмов ;)
                                                  +1
                                                  Хм, вы правы, спасибо за комментарий, буду читать в эту сторону, я как-то обычно этот момент упускал.

                                                  Не могли бы вы проиллюстрировать второй случай? Мне кажется, что функция-конструктор в js тоже может быть рассмотрена, как метаобъект :)
                                                    0
                                                    function MyClass() {/*…*/}
                                                    MyClass.answer = 42

                                                    Вот так?)
                                                      0
                                                      Я имею в виду:
                                                      function Test () { ... }
                                                      Test.prototype = любой_объект;


                                                      Так рассматривать, наверное можно, но в вашем комментарии выше вы писали про «наоборот» — метаклассы как прототипы, а не прототипы как метаклассы.
                                                0
                                                > Ruby

                                                И где там прототипы?

                                                > ActionScript 3

                                                Так они же еще в версии 2 вроде перешли на классы, нет?
                                                  0
                                                  > Так они же еще в версии 2 вроде перешли на классы, нет?

                                                  Это банальный сахар поверх prototype-based OO, что и неудивительно, ведь ActionScript растет из ECMAScript.

                                                  help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f3f.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7fa3
                                                    0
                                                    Не совсем. В 2 и тем более 3 версии прототипы — не более чем implementation details. Это к тому, что сахар — это факультативное удобство — (хочешь — используй, хочешь — нет), а классы в AS2 и AS3 — это более-менее обязательное требование.
                                                      +1
                                                      В AS2 классы были реализованы поверх прототипного наследования и при компиляции код неявно преобразовывался из class-based в prototype-based.
                                                      О чём, кстати, упоминается в статье по вами приведённой ссылке:

                                                      It’s important to understand that the underlying inheritance mechanism did not change between ActionScript 1.0 and ActionScript 2.0. ActionScript 2.0 merely added a new syntax for defining classes. The prototype chain works the same way in both versions of the language.


                                                      Для AS3 была написана новая виртуальная машина, названная AVM2 и там уже реализовано честное class-based ООП. Хотя и оставлена опциональная возможность динамической декларации полей и методов.
                                                        0
                                                        Угу, только почему-то prototype оттуда никуда не делся. А почему? А потому что ActionScript 3.0 — это, цитирую, «Completely conforming implementation of the ECMAScript fourth edition draft specification».
                                                    +2
                                                    В защиту JS хотел бы сказать, что он позволяет написать свою реализацию примесей и наследования, с которой было бы удобно работать, хороший пример, на мой взгляд, в AtomJS: github.com/theshock/atomjs/blob/master/Docs/Class/Class.md

                                                    Опять же, это означает, что нужно тащить дополнительные соглашения в код. Сравните:

                                                    Ruby
                                                    class GameObject; end
                                                    module Jump; end
                                                    module Run; end
                                                    
                                                    class DuckFromMario < GameObject
                                                       include Run, Jump, Fire
                                                    end
                                                    
                                                    duck1 = DuckFromMario.new
                                                    


                                                    JS + SomeFramework
                                                     var Run = SomeFramework.createClass({
                                                         __constructor__: function(){}
                                                     });
                                                     var Jump = SomeFramework.createClass({
                                                         __constructor__: function(){}
                                                     });
                                                     var Fire = ...
                                                    
                                                    var GameObject = SomeFramework.createClass({
                                                     __constructor__: function(name) {},
                                                     
                                                    });
                                                    
                                                    var DuckFromMario = SomeFramework.createClass({
                                                     __constructor__: function(name) {}, 
                                                    
                                                     __extends__: GameObject,
                                                    __include__: [Run, Jump, Fire],
                                                     ....
                                                    });
                                                    
                                                    var someDuck = new DuckFromMario("someDuck")
                                                    


                                                    И начинается. А что во втором примере будет, если будет вызов
                                                    var someDuck = DuckFromMario() 
                                                    
                                                    , подумал ли автор фреймворка об этом?

                                                    var DuckFromMario = SomeFramework.createClass({
                                                     __constructor__: function(name) {}, 
                                                    //а что будет, если конструктор будет без параметра? А как же родитель?
                                                    // а что будет если конструктора не будет вообще?
                                                    


                                                    И, внезапно, все подобные проблемы сваливаются на голову разработчика (и пользователей) фреймворка, а не на рантайм языка, как должно было бы по хорошему.
                                                      +1
                                                      > И, внезапно, все подобные проблемы сваливаются на голову разработчика (и пользователей) фреймворка, а не на рантайм языка, как должно было бы по хорошему.

                                                      Потому что не надо натягивать C++-like OOP на язык с совершенно другим OOP.
                                                        +1
                                                        Советую посмотреть видео с YUIConf 2011 «From one, many; from many, one — class inheritance and composition in YUI, Luke Smith», когда его выложат в YUI theater.
                                                        murr правильно говорит, было бы лучше еслиб всё это было в языке, а не фрэймворках. И все кто это делает отлично разбираются в жаваскриптовом ООП, и к счастью не только в нём.
                                                          +1
                                                          > было бы лучше еслиб всё это было в языке, а не фрэймворках

                                                          Что было в языке? Повторю еще раз. Не надо натягивать C++-like OOP на язык с совершенно другим OOP.
                                                        0
                                                        > хороший пример, на мой взгляд, в AtomJS: github.com/theshock/atomjs/blob/master/Docs/Class/Class.md

                                                        Отлично, еще один пример в мою копилку.

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

                                                        Именно.
                                                          +1
                                                          Ага. Добавляйте. И вспомните наш разговор. Это просто удобный сахар для повторяющихся действий в прототипно-ориентированном программировании. Не больше, не меньше.
                                                          0
                                                          И начинается. А что во втором примере будет, если будет вызов
                                                          var someDuck = DuckFromMario()
                                                          , подумал ли автор фреймворка об этом?

                                                          Между прочим, это очень интересная возможность. Как разница между
                                                          var n = new Number( '15' );
                                                          и
                                                          var n = Number( '15' );
                                                          


                                                          //а что будет, если конструктор будет без параметра? А как же родитель?

                                                          И что?
                                                        +3
                                                        Lua, и если он не популярен в вебе то это не значит что он не популярен.
                                            +6
                                            Я думаю у каждого JS программиста есть в загашнике подобная история.
                                              0
                                              Дык это первое, что проверяешь, если возникают «необъяснимые» ошибки — глобальные (пропущен var) и статические (объявленные через прототип) переменные.
                                              • UFO just landed and posted this here
                                                  0
                                                  Буквально сегодня такое же нашел у себя. Но это еще только писалось, так что никакого вреда
                                                  0
                                                  Пишите на Haskell, там не ошибетесь :)
                                                    +10
                                                    ? Неужто то, что я пропустил один var делает меня ущербным?

                                                    — Что может быть хуже, чем откусить яблоко и обнаружить там червяка?
                                                    — Откусить яблоко и обнаружить там полчервяка!

                                                    А по-моему такие цейтноты очень интересны. Концентрация 200%!
                                                      0
                                                      Есть отличная штука, которая помогает отловить неопознанные глобальные переменные – detect-global, но она не сгодится для server-side в текущей реализации.
                                                        +1
                                                        Недавно начал работать с Javascript'ом. Пишу в строго настроенной WebStorm по TDD-методологии, без 'use strict' не обходится ни одна функция, весь код перепроверяю через jslint. Для меня это пока минимальный набор методов, позволяющих хоть какой-то быть уверенным в своем js-коде. Поленился написать тесты — код ломается, поленился проверить jslint'ом — код ломается… это ужас какой-то! :)
                                                        • UFO just landed and posted this here
                                                            +1
                                                            Я бы назвал его недоделанным. Но при определенной дисциплине на нем можно писать работающий код.
                                                            У него есть плюс, который в значительной степени перекрывает все эти минусы: на нем можно написать приложение, которое будет работать на большинстве платформ (с помощью браузера). Для меня это оказалось очень удобным. :)
                                                              0
                                                              альтернатива?
                                                                0
                                                                TDD и статический анализ у нас теперь как БСДМ проходят?
                                                                Интересное кино!
                                                                • UFO just landed and posted this here
                                                                    0
                                                                    Да, перед тем, как начинать реализовывать новую фичу или фиксить баги я разрабатываю тесты для как можно более полного покрытия. Иногда бывает под сотню тестов для очередной feature, для багов обычно меньше.

                                                                    Если в результате доработок должны появиться новые методы, используемые в тестах, то перед написанием тестов создаются эти методы с пустыми телами. Позднее добавляется реализация.

                                                                    Как результат — после того, как все тесты начинают проходить, я могу быть уверен, что задача выполнена.

                                                                    На мой взгляд БСДМ — это попытка фиксить баги/добавлять функционал без предварительно написанных тестов.
                                                                    • UFO just landed and posted this here
                                                              +2
                                                              В том числе именно поэтому Гугл ваяет Dart на замену JS.

                                                              +3
                                                              За что люблю Python — прострелить в нём ногу гораздо сложнее, обычно.

                                                              Всё-таки есть что-то нехорошее в том, что глобальные переменные делать легче, чем локальные.
                                                                +1
                                                                Поглядел MelonCard. Какой-то неимоверный треш. На любые данные сообщает, что нашел меня на тысячах сайтов.
                                                                  +7
                                                                  Не могу найти именно про переменные, но вот почти в тему:

                                                                  image
                                                                  Источник
                                                                    +2
                                                                    Я как-то полночи отлаживал программу на C, которая вела себя примерно так же — половина данных правильная, половина нет. Оказалось, что под буфер из double-ов я выделял память как n*sizeof(char).
                                                                    Дурацкую неотлавливаемую ошибку лёгким движением руки можно сделать в любом языке.
                                                                      +1
                                                                      При современных IDE, которые все подчеркивают/подсвечивают/выделяет рамкой, подобные ошибки делать надо уметь.
                                                                      С другой стороны, если писать код в 3-4 часа ночи — то подобные ошибки могут быть у любого профи.
                                                                      С третьей стороны, нехватка тестов дала о себе знать, вот если бы было нагрузочное тестирование… Если я правильно все понял, то проблема проявлялась когда несколько пользователей одновременно совершали запросы, а юнит-тесты все проверки наверника выполняли в однопользователском режиме.
                                                                        +1
                                                                        При современных IDE, которые все подчеркивают/подсвечивают/выделяет рамкой, подобные ошибки делать надо уметь.
                                                                        ну, некоторые люди, которые считают(ся|_себя) профессиональными инженерами, считают IDE ненужным излишеством, которое якобы только мешает жить. Ну и натурально используют вместо их всякие блокноты, разработчики плагинов поддержки всяких ЯП к которым считают ненужным излишеством уже подсветку хотя бы вхождений слова под курсором, не говоря про отсеивание паразитных вхождений. Как-то так, да.
                                                                        0
                                                                        просто нужно всегда писать var
                                                                          0
                                                                          думаю запрет использования глобального контекста (объявление переменных без var) должно ложится на сам интерпретатор/компилятор.
                                                                          например в JScript.NET в параметрах компилятора существует опция fast, которая и так включена по-умолчанию.
                                                                          так для кода
                                                                          initial = extractVariables(req.body);
                                                                          

                                                                          выдалась бы ошибка
                                                                          error JS1135: Variable 'initial' has not been declared

                                                                          причем во время компиляции, а в продакшене и при выполнении приложения
                                                                            0
                                                                            *не в продакшене
                                                                            –1
                                                                            Я просто оставлю это здесь:
                                                                            github.com/tav/nodelint
                                                                            github.com/dannycoates/node-inspector
                                                                              0
                                                                              Сочувствую, отладка асинхронного кода и впрямь бывает болью в заднице.
                                                                              «нужно было использовать CoffeeScript» мне захотелось написать сразу после прочтения заголовка :)
                                                                              Удачи вам с проектом :)

                                                                              Only users with full accounts can post comments. Log in, please.