Что дал переход с SVN на Git или Git как ключ для синергии хороших практик
В первой части я расскажу о своих ощущениях после перехода на Git. Если вы уже знакомы с ним, то возможно вам следует перейти сразу ко второй части, где я попытался снизить градус слащавости и пафоса и перейти уже наконец то к делу. Во второй и третьей части я коснусь практических вопросов, которые проливают свет о влияние гита на культуру разработки.
Дифирамбы Git’у
Git меняет представление о назначение VCS. Поднимает культуру работы с кодом проекта. История изменения кода не менее важна, чем сам код, а может даже более. Git предоставляет возможности выставить акцент на истории изменения кода. В системе контроля версий начинает формироваться слой документации и требований к продукту в самой непосредственной близости к коду. И немаловажным фактором является возможность следовать новым нехитрым практикам работы с репозиторием и кодом, которые дисциплинируют разработчика. Что положительно сказывается на коде. Код становится более логичным, красивым и вылизанным.
- Ветки создаются под каждую задачу или просто для проверки какой-либо идеи;
- Коммиты создаем часто, очень часто. Хотим ли особо акцентировать внимание на изменение в коде либо просто закончили итерацию, на все один ответ — коммит. В Git нет проблемы зафиксировать каждый логический блок изменений в рамках задачи отдельным коммитом. В SVN такое желание приведет к неконсистентному состоянию кода в центральном репозиторие;
- Увлеклись, написали кучу кода, и вот у нас в одном файле изменения, которые относятся к разным логическим частям? В Git это совершенно не проблема — разные логические части одного файла Git позволяет зафиксировать разными коммитами;
- Забыли написать тесты и проверить их работу до написания функционала? Не проблема, пишем тесты и новый коммит вставляем перед коммитом задачи;
- Хотим отправить изменения в центральный репозиторий, но перед этим избавиться от избытка лишней информации и вместо 15 коммитов, отправить 4 сгруппированных? Все верно — и это не проблема.
- Я уже не говорю про классические преимущества DVSC, такие как совместная работа над одной задачей разных членов команды.
Наши практики по работе с репозиторием кода
1. | На первом шаге у нас была одна постоянная ветка, бранчи использовались очень эпизодически и не было стабильной версии продукта. Да и так бывает. Бывало даже так, что одним коммитом фиксировалось сразу несколько задач. С этого мы начинали. | ||
2. | Потом появились ветки SVN (до SVN использовали VSS и Perforce). SVN — хорошая система. Какое-то время использовали собственную модель бранчевания, потом сдались и перешли на стандартную раскладку trunk, tags, branches. В branches мы создавали ветки только под большие задачи. Прошло много времени, но я хорошо помню боль и отчаянье от работы с ветками в SVN. Это было долго — долго создавать ветки, долго переключать, долго смотреть лог, а слияния веток превращались в испытание. Но с проблемами можно было мириться, если работать только с долго живущими ветками. | ||
3. | А следом появилось ревью кода. Это была практика, которая все перевернула. Сейчас мне сложно представить разработку без ревью. | ||
4. | Идеальным дополнением к ревью кода стал Git. Модель Git (DVCS) как нельзя лучше подходит для ревью кода. Следующие пункты раскроют это утверждение. | ||
5. |
Первым делом мы ввели правила написания текста коммита. Как писать коммит у нас жестко формализовано. Сами правила начали появляться еще при SVN, но только переход на Git заставил к ним относиться строго. Главное требование гласит: текст коммита обязан содержать мотивацию принятия решений и изменений, присутствующих в коде коммита. Перед выкладыванием на сервер каждая задача проходит ревью кода. Ревью кода выполняется с помощью инструмента CodeReviewer. И вот тут основной момент — ревьюверу задачи должно быть понятно все только на основе информации из коммита: непосредственно кода и текста описания коммита. Если возникают вопросы, то описание коммита перерабатывается. Можно найти множество плюсов подобного подхода:
Раньше типичные коммиты были вот такими:
Полный зоопарк стилей и практически полное отсутствие информативности. Чтобы понять, что же сделано в коммите, необходимо смотреть код и собирать по крупицам информацию из разных источников. Разработчики, которые заползали в такой код, могли многократно ходить по одним и тем же граблям. А про автора думать самые нелицеприятные вещи, задаваясь вопросом под какими запрещенными веществами код был написан. Ирония заключается в том, что нередко рассерженный разработчик является автором злосчастного коммита. Теперь текст коммита стал вот таким:
При этом мы придерживаемся правила — код должен документировать сам себя. Комментарий в коде — признак плохого кода. Цель описания в коммите — не описание изменений, а описание почему именно так, а не иначе. В тексте коммита можно описать альтернативные варианты решения с плюсами и минусами. К слову сказать, текст коммита у нас так же ревьювируется как и код. В SVN это правило было тяжело соблюдать. Как правило, задача уходила на сервер одним коммитом, что бы не получить в централизованном репозитории не консистентного состояния. Описание всех изменений в одном коммите теряет свою красоту и эффективность. В git’е каждую задачу можно декомпозировать до небольших логических шагов, о чем в следующем правиле. |
||
6. |
Правила оформления задачи в ветках.
Каждую задачу, как правило, можно декомпозировать на несколько шагов. Отделив, например, рефакторинг от непосредственно кода, исправляющего ошибку. Каждый шаг — отдельный коммит в отдельной ветке задачи. В последствии вся ветка попадет в центральный репозиторий, сохраняя для поколений структурированное решение задачи. Теперь даже коммит, который был выполнен «заодно» и не являющийся непосредственно необходимым для закрытия задачи, находится в контексте задачи (в той же ветке), позволяя видеть цель и мотивацию автора.
Задача в SVN — это большой, размазанный по разным модулям патч. Больших усилий стоит разобраться в таком патче. Гораздо проще, когда каждая задача — это структурное решение, и можно пробежаться по отдельным шагам. В таком решении отделяются «зерна от плевел».
Репозиторий стал неотъемлемой частью кода проекта.
Пример: По ходу ревью в код могут вносится изменения, каждое такое изменение может вносится squash коммитом. Это коммит, который в последствие с помощью команды rebase можно объединить с другим коммитом (что бы ощутить мощь Git достаточно пробежаться по оглавлению способов изменить историю https://git-scm.com/book/ru/ и http://97things.oreilly.com/wiki/index.php/Record_your_rationale). |
||
7. | Правило «юнит тесты до функциональности». Это правило говорит о том, что юнит тесты должны располагаться в дереве коммитов до исправления ошибки, которую они тестируют. В результате всегда можно проверить работу юнит теста до исправления ошибки и после. Убедиться, что тест и исправление написаны верно.
Пример: |
Про культуру
«В первые годы работы над ракетной техникой практически никто из руководителей, критикующих завод, не мог конкретно сформулировать, что нужно сделать для повышения культуры производства, определить роль каждого начальника цеха, мастера и рабочего. Было слишком много общих решений. Устинов беспощадно расправлялся с начальниками цехов и производств за грязь и бескультурье. При посещениях завода он начинал с туалетов. Обычно в цехах задолго до подхода к туалету разносился характерный «аромат». В самих туалетах надо было ходить по лужам. Устинов приходил в ярость и гремел: «Какой сортир, такой и начальник цеха. Пока не добьетесь образцовой чистоты в своих сортирах, не будет чистоты и в цехах».С тех пор прошло очень много лет. Проблема чистоты общественных туалетов на наших заводах и в институтах так же, впрочем, как и в стране в целом, не решена. Это оказалось куда труднее, чем создать самое грозное ракетно-ядерное оружие и завоевать мировой приоритет в космонавтике.Явный дефицит культуры, общей производственной чистоты и гигиены до сих пор является одной из причин низкого качества многих отечественных изделий. За время войны и в последующие годы забота об элементарном комфорте в цехах, создание рабочему достойной и привлекательной общей обстановки считались излишней и непозволительной роскошью. Затраты на чистоту, комфорт, элементарный сервис с лихвой окупаются повышением производительности и качества. «
Несколько полезных ссылок
- http://git.kernel.org/cgit/git/git.git/tree/Documentation/SubmittingPatches?id=HEAD
- https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
- https://wiki.openstack.org/wiki/GitCommitMessages
- http://stackoverflow.com/questions/3580013/should-i-use-past-or-present-tense-in-git-commit-messages
Самый простой способ автоматической настройки профиля почты в вашем приложении Delphi
Практически каждый серьезный почтовый клиент имеет в своем составе полезную функцию автоматической настройки профиля почты. Эта функция очень сильно упрощает жизнь рядовым пользователям, которых в
Что в Windows 3.1 делала кнопка "Игнорировать" в диалоге GPF?
Это перевод What did the Ignore button do in Windows 3.1 when an application encountered a general protection fault? Автор: Реймонд Чен.
Когда ваше приложение вызывало General Protection Fault (GPF, общее нарушение защиты, сегодня известное как Access Violation) в Windows 3.0, система показывала такой диалог об ошибке:
Application error |
CONTOSO caused a General Protection Fault in module CONTOSO.EXE at 0002:2403 |
Close
|
В Windows 3.1 же вы получали такой диалог при выполнении некоторых условий:
CONTOSO |
An error has occurred in your application. If you choose Ignore, you should save your work in a new file. If you choose Close, your application will terminate. |
Close
Ignore
|
Хорошо, мы знаем, что делает кнопка «Close» (закрыть). Но что делает кнопка «Ignore» (Игнорировать)? И при каких-таких условиях она появляется?
Говоря грубо, кнопка «Игнорировать» появляется, если:
- Ошибка является общим нарушением защиты (GPF),
- Инструкция, вызвавшая ошибку, не принадлежит ядру или менеджеру памяти,
- Инструкция, вызвавшая ошибку, является одной из следующих (возможно, с одним или несколькими префиксами):
- Операции с памятью:
op r, m
;op m, r
; илиop m
. - Строковые операции:
movs
,stos
, etc. - Загрузки селектора:
lds
,les
,pop ds
,pop es
.
- Операции с памятью:
Если эти условия выполняются, то показывается кнопка «Игнорировать». А если вы нажмёте на эту кнопку, то ядро выполнит следующее:
- Если инструкция, вызвавшая ошибку — это загрузка селектора, то регистр назначения просто обнуляется;
- Если инструкция, вызвавшая ошибку — это
pop
, то указатель стека увеличивается на два байта; IP
переходит на следующую машинную команду;- Выполнение возобновляется.
Другими словами, ядро выполняло некий ассемблерный эквивалент для ON ERROR RESUME NEXT
.
Так, вы можете подумать: «Как это вообще могло работать? Вы же просто пропускаете выполнение случайных команд!». Как бы это ни было странным, но эта идея была настолько сумасбродной, что она работала — или, по крайней мере, работала достаточно часто. Вам могло понадобится нажать «Игнорировать» дюжину раз, но в итоге был хороший шанс, что плохие значения регистров затрутся хорошими (и, вероятно, это не займёт много времени, поскольку у 8086 было крайне мало регистров), и программа продолжит вроде-как-нормально выполняться.
Полное безумие.
Упражнение: почему коду не нужно было знать, как игнорировать инструкции перехода и условного перехода?
Бонусный тривиальный факт: Разработчик, реализовавший эту сумасшедшую возможность, был Дон Корбитт — автор Доктора Ватсона.
6 лет WebDelphi.ru
Очередная веха в жизни блога. 15.07.2015 исполнилось 6 лет блогу webdelphi.ru. При этом я бы не назвал 2015 год продуктивным в плане работы в блоге
Тестовое задание от Infoteсs
Недавно получил тестовое задание для трудоустройства от компании Infotecs.
Спешу им поделиться с дорогими читателями.
Необходимо разработать клиент-серверное приложение, работающее по следующему сценарию:
- Клиент после запуска ожидает ввода пользователя.
- Пользователь вводит число в клиент.
- Клиент отправляет число в сервер при помощи протокола TCP и ожидает ввода пользователя.
- Сервер раскладывает число на простые множители и отправляет клиенту ответ.
- Клиент сообщает результат пользователю.
Приложение должно удовлетворять следующим требованиям:
- Клиент должен быть Android-приложением.
- Пользователь может ввести в клиенте несколько чисел, не дожидаясь получения ответов от сервера.
- Сервер должен поддерживать одновременное обслуживание нескольких клиентов.
- Исходный код должен быть хорошо оформлен и иметь комментарии (т.е. должен быть написан так, как вы его пишете всегда).
- Желательно снабдить приложение модульными тестами.
- Сервер должен быть написан в виде Android-сервиса.
- Клиент и сервер должны быть отдельными apk.
Вот ссылка на итоговый проект https://github.com/petrovichtim/InfotecsTestTask
В проекте реализовано 2 модуля клиент (обычная Activity) и сервер (IntentService).
Экран работы клиента:
Об альтернативе Application.ProcessMessages для TWebBrowser и разрыве стека выполнения
Использование Application.ProcessMessages может вызывать неожиданные проблемы. Особенно это актуально, когда используются оконные windows сообщения для разрыва стека выполнения. Но нашелся способ, которое позволяет не выбирать все сообщения из очереди. На первый взгляд, решение даже работает, однако буду рекомендовать никогда его не использовать и всегда использовать Application.ProcessMessage и об этом ниже.
Очевидно, что WebBrowser обрабатывает какие-то сообщения. Но поиск их казался гиблой идеей. В действительности оказалось, что ни так все плохо. С помощью Window Detective было обнаружено, что в момент загрузки происходит подозрительная посылка сообщений окну с именем класса ‘Internet Explorer_Hidden’. Решил проверить и выбрал из очереди оконных сообщений в момент загрузки документа сообщения предназначенные только этому окну. К моему удивлению — все заработало.
Сообщения получаемые окном IE |
TMyWebBrowser = class(TWebBrowser)
protected
procedure WBProcessMessage;
procedure InternalSetValue(const AValue: string);
public
procedure SetValue(const AValue: string); // Входная точка в примера
procedure WaitWB;
end;
function EnumWindowsToFindIEHiddenProc(AHandle: HWND; AParam:NativeInt): boolean; stdcall;
var
IEHiddenHandle: Hwnd;
implementation
procedure TMyWebBrowser.SetValue(const AValue: string);
begin
InternalSetValue(AValue); // выполняем каким либо способом присваивание разметки
WaitWB; // Ждем завершение загрузки документа.
FooFunction; //Какой то функционал для работы которого необходим полностью загруженные html документ.
end;
procedure TMyWebBrowser.WaitWB;
begin
//Как то так обычно выглядит ожидание пока документ полностью не загрузиться
while HTMLDocument2.readyState <> 'complete' do
begin
WBProcessMessage; // выбираем только нужные сообщения
//Forms.Application.ProcessMessages; // выбираем все сообщения из очереди
end;
end;
procedure TbtkHTMLEditor.WBProcessMessage;
var
msg: Windows.tagMSG;
processID : THandle;
begin
IEHiddenHandle := 0;
processID := GetCurrentProcessId;
if EnumWindows(@EnumWindowsToFindIEHiddenProc, processID) then // �щем хендл окна IE в нашем процессе перебирая все окна
if IEHiddenHandle <> 0 then // Проверяем найденный хендл валидный
if PeekMessage(msg, IEHiddenHandle, 0, 0, PM_REMOVE) then // извлекаем из очереди оконных сообщений все сообщения для окна IEHiddenHandle
begin
Windows.DispatchMessage(msg); // Передаем извлеченные сообщения окну IE
end;
end;
function EnumWindowsToFindIEHiddenProc(AHandle: HWND; AParam:NativeInt): boolean;
var
processId: NativeInt;
classbuf: array[0..255] of Char;
const
IEWndClassName = 'Internet Explorer_Hidden';
begin
result := true;
if Windows.GetWindowThreadProcessId(AHandle,@processId) <> 0 then
begin
if AParam = processId then
begin
GetClassName(AHandle, classbuf, SizeOf(classbuf));
if lstrcmp(@classbuf[0], @IEWndClassName[1]) = 0 then
begin
IEHiddenHandle := AHandle;
result := false;
end;
end;
end;
end;
Как обоблачиться с YandexDisk — 2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet
Данная статья является продолжением предыдущей§2.5. Протокольные компонентычасть 2. TREST~ и TDataSet
Часть I. Теория
2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные
Первая часть задачи
Кажется я первый раз попал в тупик.
Не то, чтобы я сильно умный, но и задачка — не «Балтика 9».
Первая часть задачи выглядела вот так:
Что
http://alexander-bagel.blogspot.com/2015/04/18-gunsmoker.html