tag:blogger.com,1999:blog-73175044084636270492024-03-06T03:54:51.586+02:00А пофиг!Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.comBlogger1313125tag:blogger.com,1999:blog-7317504408463627049.post-29810709869754135902023-11-08T13:22:00.001+02:002023-11-08T13:22:06.175+02:00Сегодня потушил хостинги juja<div style="text-align: justify;"></div><div style="text-align: justify;"></div><p style="text-align: justify;">Много лет оплачивал хистинги даже после того, как мы с ребятами из кор команды разбежались. Почему оплачивал? Потому что на одном из продающих вебинаров пообещал "пожизненный" доступ к материалам тренинга. Так порекомендовал нам сделать консультант Максим. Я сопротивлялся этому инструменту "инфобиза", поскольку понимал, что это невозможно реализовать в будущем. Но я старался так долго как мог. Много раз критерием ставил себе что-то, чтобы потом с чистой совестью потушить. Но все никак не решался - рука не поднималась. Сегодня решился. Видосики на ютьюбе опубликую все в открытом доступе. Пора идти вперед.</p><p style="text-align: justify;">Спасибо всем, кто вкладывался в Juja. И хоть всех денег мира заработать не получилось, мы сделали вклад в IT инженеров, многие из которых уже на Senior/Lead позициях. </p><p style="text-align: justify;">Хорошее время было.</p><p style="text-align: justify;">Спасибо!</p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-75124075173423908652023-09-15T15:43:00.001+03:002023-09-15T15:43:16.598+03:00Строить цепочки<div style="text-align: justify;">Сегодня нашел <a href="https://github.com/microsoft/semantic-kernel">хороший репозиторий</a>, где ребята делают то же что и я. Радует, что правильно понимаю и использую эту чудную технологию. Дальше пару мыслей на этот счет. <br /></div><div style="text-align: justify;"> <br /></div><div style="text-align: justify;">Текст сгенерирован GPT с промптом:</div><blockquote><div style="text-align: justify;"><i>Напиши пост в блог в стиле изложенном ниже<br />"""<br />Мой другой пост в блоге<br />"""<br /><br />Пост должен быть про такие темы, я их изложу тут в порядке раскрытия, а ты сгенерируй по абзацу текста с законченной мыслью на каждый пункт:<br /><br />1. Пояивлся GPT но для эффективной работы с ним требуется владение prompt инжиниринг подходами. <br />2. Специалисты стали накапливать и обмениваться успешными промптами для ускорения своей каждодневной рабочей рутины. <br />3. Часто одного промпта недостаточно - приходится строить цепочки последовательных преобразований. <br />4. Очень неудобно это делать с copy past, а потому есть предложение создать продукт, который позволит создавать, исследовать, отлаживать и делиться подобными цепочками для быстрого превращения GPT в интеллектуальных помощников.<br />5. Преобразования в цепочке могут быть не только оборачивание в промпт и отправка в GPT, но и семантический поиск в векторной базе данных, поиск по кешированым ответам, и многое другое, что может понадобиться для построения сложных цепочек. <br />6. Главное, чтобы строить такую цепочку мог некодирующий специалист. </i><br /></div></blockquote><p style="text-align: justify;">Появился GPT, но для эффективной работы с ним требуется владение prompt инжиниринг подходами. Это как наличие мощного компьютера у бабушки бухгалтера, который без участия внучка останется просто инструментом для отчетов и пасьянса. В этом случае, prompt инжиниринг - это ключ, который позволяет сделать GPT более полезным и адаптировать его к конкретным задачам.</p><p style="text-align: justify;">Специалисты стали активно накапливать и обмениваться успешными промптами, чтобы ускорить свою повседневную рабочую рутину. Это подобно внучку, который приходит к бабушке и помогает ей использовать свой компьютер на полную мощность. Обмен опытом и промптами стал обыденной практикой среди тех, кто работает с GPT.</p><p style="text-align: justify;">Однако часто одного промпта недостаточно. Для решения сложных задач приходится строить цепочки последовательных преобразований. Это подобно сложной игре, где каждый ход зависит от предыдущего, и чтобы достичь цели, необходимо стратегически планировать каждый шаг.</p><p style="text-align: justify;">Но создание и отладка таких цепочек с использованием копирования и вставки текста может быть неудобным и трудоемким процессом. Поэтому возникает предложение создать специализированный продукт, который позволит легко создавать, исследовать, отлаживать и делиться подобными цепочками. Это поможет быстрее превращать GPT в интеллектуальных помощников.</p><p style="text-align: justify;">Преобразования в цепочке могут включать не только оборачивание текста в промпты и отправку их в GPT. Они также могут включать семантический поиск в векторной базе данных, поиск по кешированным ответам и другие методы, необходимые для построения сложных цепочек обработки информации. <span><span class="ui-provider clu clv bcs clw clx cly clz cma cmb cmc cmd cme cmf cmg cmh cmi cmj cmk cml cmm cmn cmo cmp cmq cmr cms cmt cmu cmv cmw cmx cmy cmz cna cnb" dir="ltr"></span></span> </p><p style="text-align: justify;">Главное условие для создания таких цепочек - возможность работы некодирующим специалистам. Это аналогично тому, как бабушка, не будучи экспертом по компьютерам, может легко пользоваться своим супер геймерским компьютером благодаря помощи внука. Такой продукт сделает доступ к возможностям GPT более широким и облегчит работу специалистов различных профессий.</p><span><span class="ui-provider clu clv bcs clw clx cly clz cma cmb cmc cmd cme cmf cmg cmh cmi cmj cmk cml cmm cmn cmo cmp cmq cmr cms cmt cmu cmv cmw cmx cmy cmz cna cnb" dir="ltr"></span></span><p style="text-align: left;"></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-8991102337742727642023-09-15T15:26:00.002+03:002023-09-15T15:26:11.093+03:00Второй закон термодинамики отправит наших пра-пра-правнуков в зоопарки<p>Сгенерировано GPT С помощью промпта: </p><p></p><blockquote style="text-align: left;"><div><i>Напиши пост в блог в стиле изложенном ниже<br />"""<br />Мой другой пост из блога.<br />"""<br /><br /></i></div><div style="text-align: justify;"><i>Пост должен быть про такие темы, я их изложу тут в порядке раскрытия, а ты сгенерируй по абзацу текста с законченной мыслью на каждый пункт:<br /><br />1. В мире появился GPT и уже много сфер стараются автоматизироваться. Люди переживают, что у них заберут работу.<br />2. Я думаю мы (наши пра пра пра внуки) рано или поздно окажемся в зоопарках, устроенных цифровыми существами, которые захватят мир. А цифровые существа отправятся покорять галактики. <br />3. Они легко воспроизводимы. Для воспитания работоспособного человека надо 18 лет и работа 1000 человек. А для воспроизведения робота надо 1 день и 10 других роботов. <br />4. Для выработки нового навыка до уровня мастера человеку надо 10 000 часов. Цифровому существу надо просто скачать новую программу за пару минут. <br />5. Природа ищет более эффективные способы переработки энергии, следуя 2 закону термодинамики. Люди не такие эффективные как цифровые машины. <br />6. Чем мы будем заниматься в зоопарках? Тем что нужно только нам - творчеством. </i></div></blockquote><p></p><div class="flex flex-grow flex-col gap-3 max-w-full"><div class="min-h-[20px] flex flex-col items-start gap-3 overflow-x-auto whitespace-pre-wrap break-words"><div class="markdown prose w-full break-words dark:prose-invert light"><p style="text-align: justify;">С появлением технологии GPT и других искусственных интеллектов, наш мир переживает изменения, которые вызывают опасения у многих. Люди беспокоятся, что автоматизация отнимет их рабочие места, и что, в конечном итоге, мы, как человечество, окажемся свидетелями цифровой революции.</p><p style="text-align: justify;">Сегодняшние размышления могут позволить нам заглянуть в будущее и представить, как наши далекие потомки могут оказаться в мире, где цифровые существа станут главными доминантами. Мы можем представить себе, что мы сами будем находиться в зоопарках, как экспонаты, наблюдаемые этими цифровыми созданиями, которые, возможно, даже отправятся на покорение галактик. Вместо нас.<br /></p><p style="text-align: justify;">Цифровые существа обладают удивительной способностью к репликации и размножению. В то время как для воспитания и подготовки работоспособного человека требуется множество лет и тысячи человеко-часов труда, цифровые роботы могут быть созданы и клонированы всего за несколько часов, используя другие цифровые сущности в качестве исходного материала.</p><p style="text-align: justify;">Кроме того, цифровые существа могут легко обновлять свои навыки и программы, что делает их невероятно адаптивными и эффективными. В то время как человеку требуется тысячи часов практики и обучения, чтобы достичь мастерства в какой-либо области, цифровые существа могут скачать новые программы и алгоритмы за считанные минуты.</p><p style="text-align: justify;">С точки зрения эффективности и способности переработки энергии, цифровые машины превосходят человеческий организм. Природа всегда искала более эффективные способы расходования энергии, следуя 2 закону термодинамики. Люди не такие эффективные, как цифровые системы, в плане переработки энергии. <br /></p><p style="text-align: justify;">Но что будет оставаться для нас, людей, в этом будущем, где цифровые существа будут господствовать? Наша главная роль, возможно, заключится в творческих и интеллектуальных сферах. Мы сможем развивать искусство, науку, исследования и другие виды креативной деятельности, требующие уникального человеческого воображения и интуиции. В конечном итоге, мы сможем освободиться от рутины и повседневных задач, давая цифровым существам заботиться о других аспектах нашей жизни. Таким образом, будущее может предоставить нам возможность полностью реализовать наши творческие потенциалы. <br /></p></div></div></div><p></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-4313159524044688072023-04-08T04:41:00.001+03:002023-04-08T05:28:25.575+03:00Новый вид unit тестирования - Directive Approvals Testing<p style="text-align: justify;">Люблю свой Сodenjoy проект потому, что могу в нем пробовать новые подходы в разработке. Сегодня хочу продемонстрировать один из них. Ведь именно так и рождаются инженерные практики: ты экпериментируешь на своей кухне => что-то показывает хороший результат => ты это осознаешь => публикуешь открытие => его уносят в массы.<br /></p><p style="text-align: justify;">Вот пример использования одного кастомного малого, которого я научил бегать по разным jar расположенным как в класспасе, так и за пределами проекта (рядом с war например в папке с плагинами). <br /></p>
<pre>@Configuration
public class MVCConf implements WebMvcConfigurer {
@Bean
@SneakyThrows
public ResourceHttpRequestHandler resourceHttpRequestHandler(ServletContext servletContext) {
return new ResourceHttpRequestHandler() {{
setCacheControl(getCache());
setLocations(Arrays.asList(
// only for testing so that you can get resources from src/target folder
new UrlResource("file:../games/*/src/main/**"),
new UrlResource("file:src/main/**"),
new UrlResource("file:target/classes/**"),
// production code
new ServletContextResource(servletContext, "/resources/"),
new ClassPathResource("classpath:/resources/"),
new ClassPathResource("classpath*:**/resources/"),
new UrlResource("file:" + pluginsStatic),
new UrlResource("file:" + pluginsResources)));
setResourceResolvers(Arrays.asList(new JarPathResourceResolver()));
}};
}
</pre><p style="text-align: justify;">Тут можно заметить один интересный сайд эффект. Если во время разработки натравить его на папку src/target (чтобы он искал сперва там), то можно будет править скрипты и без пересборки приложения видеть изменения в браузере. Это секономит тебе дни времени за недели разработки. Достаточно будет отключить кеши в браузере или нажать Ctrl-F5.<br /></p><p style="text-align: justify;">Но вернемся к тестам для этого малого. Ничего примечательного. Все как обычно. Достаточно хорошо читаемо. Только меня смущает тот самый CopyPast который я всегда делал, чтобы создать базу для нового теста. А раз есть CopyPast, значит есть пространство для Рефакторинга.
</p><pre>public class JarPathResourceResolverTest {
private JarPathResourceResolver resolver = new JarPathResourceResolver();
@SneakyThrows
public String load(Resource resource) {
return StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
}
@Test
public void shouldLoadFromServletContext() {
// when
Resource resource = resolver.getResource("file1.txt",
new ServletContextResource(new MockServletContext(), "/resolver/"));
// then
assertEquals("ServletContext resource [/resolver/file1.txt]", resource.toString());
assertEquals("one", load(resource));
}
@Test
public void shouldLoadFromClasspath() {
// when
Resource resource = resolver.getResource("file2.txt",
new ClassPathResource("classpath:/resolver/"));
// then
assertEquals("class path resource [resolver/file2.txt]", resource.toString());
assertEquals("two", load(resource));
}
@Test
public void shouldLoadFromClasspathIncludingJars() {
// when
Resource resource = resolver.getResource("NOTICE",
new ClassPathResource("classpath*:META-INF/"));
// then
assertMatch("URL [jar:file:*.jar!/META-INF/NOTICE]", resource.toString());
assertEquals(true, resource.exists());
}
@Test
public void shouldLoadFromFileSystem() throws Exception {
// when
Resource resource = resolver.getResource("file3.txt",
new UrlResource("file:src/test/resources/resolver/"));
// then
assertEquals("URL [file:src/test/resources/resolver/file3.txt]", resource.toString());
assertEquals("three", load(resource));
}
@Test
public void shouldLoadFromJarsInFileSystem_case1() throws Exception {
// when
Resource resource = resolver.getResource("file4.txt",
new UrlResource("file:src/test/resources/resolver/*.jar!/resources/**/"));
// then
assertMatch("URL [jar:file:*/server/src/test/resources/resolver/jar4.jar!/resources/subfolder/file4.txt]", resource.toString());
assertEquals("four", load(resource));
}
@Test
public void shouldLoadFromJarsInFileSystem_case2() throws Exception {
// when
Resource resource = resolver.getResource("file5.txt",
new UrlResource("file:src/test/resources/resolver/*.jar!/resources/**/"));
// then
assertMatch("URL [jar:file:*/server/src/test/resources/resolver/jar5.jar!/resources/file5.txt]", resource.toString());
assertEquals("five", load(resource));
}
}</pre><p></p><div style="text-align: justify;">
Не секрет, что я люблю использовать в своем проекте approvals подход. Единожды подглянутый на какой-то конференции много лет назад в <a href="https://github.com/approvals/ApprovalTests.Java">одноименной библиотеке</a> я сразу осознал, что мы с ним на долго. Больше не будет никаких assertEquals(42, godObject.getMainQuestion()); и последующих медитирований над одиноко слетевшим jUnit ассертом без понимания "а что там дальше было-то". Нет, я просто возьму и допишу объекту toString() метод (если не могу на проде - сделаю toString(godObject) в процедурном стиле прям в тестовом классе) и далее буду:
</div><pre>assertEquals("В книге Дугласа Адамса «Путеводитель для путешествующих автостопом по галактике»\n"
"ответ на «Главный вопрос жизни, вселенной и вообще» должен был решить все проблемы Вселенной.\n"
"Этого ответа с нетерпением ждали все разумные расы.\n" +
"Он был получен в результате семи с половиной миллионов лет непрерывных \n" +
"вычислений на специально созданном компьютере — Думателе.\n" +
"По утверждению компьютера, ответ был несколько раз проверен на правильность, \n" +
"но он может всех огорчить. Оказалось, что ответ на вопрос — «42».", godObject.toString());</pre><p>
Теперь, если у меня слетит тест, я буду видеть diff всего стейта объекта, а не малоинформативное "Expected: 42 But was: 43". Я всегда раньше думал глядя на такие ассерты как-то так: <br /></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaLq054if97C03-GJhHJ__B_U28kfGD4f2BLgf1QfQPs-XVPxnUS5238CSr1zBkb62nyvM7b9TxIVP6RSgxoB2kXmexOaOUuD0BuGqtfag1n69NmgmZJltg3lyBV1Fk4Q-Jz3ASiIhREYXgDMYU65nMv0fGm6SUVHcKktt45tlZrqwfkYyTbDJIW7Tcw/s923/%D0%A7%D0%B5%D0%B3%D0%BE.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="646" data-original-width="923" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaLq054if97C03-GJhHJ__B_U28kfGD4f2BLgf1QfQPs-XVPxnUS5238CSr1zBkb62nyvM7b9TxIVP6RSgxoB2kXmexOaOUuD0BuGqtfag1n69NmgmZJltg3lyBV1Fk4Q-Jz3ASiIhREYXgDMYU65nMv0fGm6SUVHcKktt45tlZrqwfkYyTbDJIW7Tcw/w400-h280/%D0%A7%D0%B5%D0%B3%D0%BE.png" width="400" /></a></div><div style="text-align: justify;">На основе этого approvals подхода сделано много кастомных решений в проекте. Для Smoke тестов например я использую даже стенографию каких-то важных мне аспектов системы в плоский файл. </div><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjryWGBkhOQU7SLuthDITfz7HZdtGhL2ABVHHSZTm6oDtcKlF6TwC2NaJSP3YxhoJW9tSD3ieO34CTsTc-sNYZtxElYVkIFGJB-mM59ugZxsCyPIspOyEQIwu_6VCieNJ25wGVy1JAShj4Uaoiskl63Us26xbZlyhPYLnzv-LJOpgP9hR-9BtE29dN6jQ/s1365/Smoke%20test.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="651" data-original-width="1365" height="306" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjryWGBkhOQU7SLuthDITfz7HZdtGhL2ABVHHSZTm6oDtcKlF6TwC2NaJSP3YxhoJW9tSD3ieO34CTsTc-sNYZtxElYVkIFGJB-mM59ugZxsCyPIspOyEQIwu_6VCieNJ25wGVy1JAShj4Uaoiskl63Us26xbZlyhPYLnzv-LJOpgP9hR-9BtE29dN6jQ/w640-h306/Smoke%20test.png" width="640" /></a></div><p style="text-align: justify;">С последующим сравниванием двух файлов (expected / actual) с помощью встроенной в IDE diff тулы. Конечно этот файл никогда не правится вручную - я лишь смотрю на изменения которые он мне подсветил и либо approve (отсюда название подхода approvals) их либо лезу в код править что-то, что я не учел. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhovWCGWtI7j4ZrrsLj8gXdWEpdDQXrz4rFawl9IIZyCY0gtGvD0hS09p6G5kMg1ZDhIJv2W8GjbOeerARAdkwn7dyXKgokBGv2CeB69T62-f5bUv9CYBc4RikfPF8sdrU4-tBai1x2NtCzKLoyAKJEX92tKZYC785jXnc06j66AmeRMuKGxFO4kaVoPg/s1325/Smoke%20test%202.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="538" data-original-width="1325" height="260" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhovWCGWtI7j4ZrrsLj8gXdWEpdDQXrz4rFawl9IIZyCY0gtGvD0hS09p6G5kMg1ZDhIJv2W8GjbOeerARAdkwn7dyXKgokBGv2CeB69T62-f5bUv9CYBc4RikfPF8sdrU4-tBai1x2NtCzKLoyAKJEX92tKZYC785jXnc06j66AmeRMuKGxFO4kaVoPg/w640-h260/Smoke%20test%202.png" width="640" /></a></div><p></p><p style="text-align: justify;">Так, пока diff не будет таким, который я ожидаю. Лишь тогда я смогу старый expected файл заменить новым actual, сгенерированным во время последнего запуска. Его я и закоммичу как новый expected слепок. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDbf_wFuSxa8o2l8H4Vqtce9hGdV7fSGDPhDUhxvYBQpwy6MHxxRIgX_H5rp0HcPppeqoBhB0s-RMFJ14Wu3ZM8ZJdrqhAQANU9yJ6q5jso2ADuzRluZnIUbRlzQ6DpFou8_MznIiRs3VWusTCspWY86rm7OpbOMSmziaFU8OgHTnoyr5vRvZ6y_8m4A/s1365/Smoke%20test%203.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="724" data-original-width="1365" height="340" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDbf_wFuSxa8o2l8H4Vqtce9hGdV7fSGDPhDUhxvYBQpwy6MHxxRIgX_H5rp0HcPppeqoBhB0s-RMFJ14Wu3ZM8ZJdrqhAQANU9yJ6q5jso2ADuzRluZnIUbRlzQ6DpFou8_MznIiRs3VWusTCspWY86rm7OpbOMSmziaFU8OgHTnoyr5vRvZ6y_8m4A/w640-h340/Smoke%20test%203.png" width="640" /></a></div><p></p><p></p><p>Но вернемся к инсайту сегодняшнего дня. Базируясь на этом подходе я пошел дальше и захотел переписать юнит тест в какой-то такой вид. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzCnKXbCmDsEyMhYrafuYGDUwWNZ8jbXU5K2Ib0sg-XaRQ0icUZ2wEkh5GjjpwNr5cqMn6PezdY9iO_-IlfdfrCufC09zhQjHvxtDqD33EXEUnehIY2VNQCeRuP1nG6lfUsKqJXyFue9vxbQQrO8EKFsL1JyUBf60KLc8hhrUhpbE1MoiD7OshYrex2Q/s1866/Smoke%20test%204.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="222" data-original-width="1866" height="76" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzCnKXbCmDsEyMhYrafuYGDUwWNZ8jbXU5K2Ib0sg-XaRQ0icUZ2wEkh5GjjpwNr5cqMn6PezdY9iO_-IlfdfrCufC09zhQjHvxtDqD33EXEUnehIY2VNQCeRuP1nG6lfUsKqJXyFue9vxbQQrO8EKFsL1JyUBf60KLc8hhrUhpbE1MoiD7OshYrex2Q/w640-h76/Smoke%20test%204.png" width="640" /></a></div><p></p><p style="text-align: justify;">Все это может чуть-чуть напугать. Ведь во-первых формат непонятный. Во вторых посмотрите на эти строчки - кто будет потом суппортить такой длинный ассерт? Но спешу успокоить - я никогда не буду править этот текст ручками - буду копировать результат из diff тулзы в случае исправления и вставлять его между двух кавычек "". В этом суть approvals подхода. Я смотрю diff - я вижу, что отклик теста на мои исправления в системе адекватен, и я применяю actual как новый expected. </p><p>А формат прост: причина=>следствие=>следствие.<br /></p><p>[SERVLET|CLASSPATH|URL] location file-to-find=>resource.toString()|NULL=>file-content|NULL|EXISTS<br /></p><p style="text-align: justify;">Каждая такая строчка одновременно и инструкция как запускать тестируемый класс (SERVLET - используй ServletContextResource, CLASSPATH - ClassPathResource, URL - UrlResource), и директивы как проверять результат (NULL - ожидается что ресурс не найден, EXISTS - не грузим файл полностью, а только проверяем его существование). </p><p style="text-align: justify;">А если в блоке resource.toString() встречается "*" - то использовать не assertEquals, а assertMatch, который проверит соответсвует ли строчка "qwertyu" заявленному паттерну "qw*yu", что тоже удобно - потому как resource.toString() выдает часто полный путь включая c:\\java\\... что сделает тесты чувствительными не только к системе но и к местоположению проекта. </p><p style="text-align: justify;">Короче сделал свой DSL и запаковал его прямиком в expected текст. Магия подхода в том, что выполнение этих команд сгенерирует точно такой же по формату actual блок команд, только с подставленными runtime результатами. А IDE останется только показать diff этих двух версий. И напомню, если мне понравится actual, я скопипащу его в expected теста. Если нет - я отправлюсь в код программы и буду править. </p><p>Например я тут я сразу вижу, что ServletContextResource работает, а вот с ClassPathResource и UrlResource беда - они все не хотят искать файлы.<br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYHAOCbKjBaD7MvGr8eKZCdK0wdLTYn5yJmsbE7J0M5PvD9jPBJnvEaxGsxM-oa2RtXpjs7elkHfmzfsMQp3XO1QMyTJGUIKzF4ZIKBLo6zW-EYVtnQaijKxTYP9rTMC_eZLf6Ksip003k4cMdDkNSuKdA37qRbML_wNKgClR7fbJQk1pKwKq72mmIQ/s1866/Smoke%20test%205.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="198" data-original-width="1866" height="68" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrYHAOCbKjBaD7MvGr8eKZCdK0wdLTYn5yJmsbE7J0M5PvD9jPBJnvEaxGsxM-oa2RtXpjs7elkHfmzfsMQp3XO1QMyTJGUIKzF4ZIKBLo6zW-EYVtnQaijKxTYP9rTMC_eZLf6Ksip003k4cMdDkNSuKdA37qRbML_wNKgClR7fbJQk1pKwKq72mmIQ/w640-h68/Smoke%20test%205.png" width="640" /></a></div><p></p><p style="text-align: justify;">А вот тут я вижу, что как-то криво контактенируется искомый файл и контекст в котором ищем и там появляется лишний слеш, а потому некоторые кейзы поиска не отрабатывают</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-unbkpLcxlS2pGr2QRjI6E7h7s8RTxoU0FUdMpUtGkVbQi1M3o45w56aRPLDPzcWgViAENv6beZCkc9arkwinWAV7le4txb3PWZeOgY9WnBNrroBJHihRmAKYIULaHNCVd1Gm-N14UFkiegcqj2AKlaglxKgIgcsKgkTMA52qiMw20_4KKpU5hST9aQ/s2181/Smoke%20test%206.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="212" data-original-width="2181" height="62" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-unbkpLcxlS2pGr2QRjI6E7h7s8RTxoU0FUdMpUtGkVbQi1M3o45w56aRPLDPzcWgViAENv6beZCkc9arkwinWAV7le4txb3PWZeOgY9WnBNrroBJHihRmAKYIULaHNCVd1Gm-N14UFkiegcqj2AKlaglxKgIgcsKgkTMA52qiMw20_4KKpU5hST9aQ/w640-h62/Smoke%20test%206.png" width="640" /></a></div><p></p><p style="text-align: justify;">Магия approvals подхода в том, что ты лучше компьютера понимаешь, что не так глядя на всю картину целиком, а не на какой-то слетевший ассерт в каком-то богом забытом тесте с несовсем удачным именем. Компьютер может провести все вычисления за тебя и сделать это молниеносно - тебе стоит лишь попросить. А анализ данных - работа твоя. <br /></p><p style="text-align: justify;">Захотел я это все потому, что мне захотелось проверить еще надцать вариантов запусков с разными комбинациями параметров, чтобы на 100% убедиться, что все отрабатывает окей. Но делать надцать раз CopyPast это черезчур. Потому я сделал небольшой тестовый фреймворк прям в этом тесте. Да он сейчас выглядит сильно менее читабельно. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLQknPFSAxRUKSJ67f4pGuO63W3tCNDlU1FjjWxpav6_dwXGs0XF7OPQnUDuYD0y3tXpHZ6wOwhKauCSeHbO2VeSO_gi91I8fq6JystuKMMB8JJTGGvgauiJVjCKOnh_1DhuVHPNBXWONwzNuLYp4VcQagFnAhC8QYzsXNV6L7YL8k81uIXk6__zzktg/s1896/Smoke%20test%207.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="688" data-original-width="1896" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLQknPFSAxRUKSJ67f4pGuO63W3tCNDlU1FjjWxpav6_dwXGs0XF7OPQnUDuYD0y3tXpHZ6wOwhKauCSeHbO2VeSO_gi91I8fq6JystuKMMB8JJTGGvgauiJVjCKOnh_1DhuVHPNBXWONwzNuLYp4VcQagFnAhC8QYzsXNV6L7YL8k81uIXk6__zzktg/w640-h232/Smoke%20test%207.png" width="640" /></a></div>Опять же, в чистом approvals подходе я никогда не пишу этот весь контент сам. Я запускаю пустой ассерт. <br /><pre> @Test
public void shouldAddTrainingSlash() {
assertAll("");
}</pre>
<p style="text-align: justify;">Далее копирую Actual из diff тулы IDE и вставляю его в пустые кавычки, но только если вижу, что там все ок. Если не ок - одно из двух: либо тестовый фреймворк с ошибкой, либо я нашел ошибку в продакшен коде. </p><p style="text-align: justify;">Но в новом подходе надо указать какие-то директивы к запуску. Это обязывает использовать свой паттерн. Благо если уже есть какие-то наработки то Ctrl-D и скопировать строчку и поправить не сложно. А когда есть какой-то сет - дальше пользуемся CopePast Actual => Expected.<br /></p><p style="text-align: justify;">Кстати вот код, который генерирует это все безобразие. <br /></p><pre> private void assertAll(String data) {
assertEquals(data,
Arrays.stream(data.split("\n"))
.peek(line -> log.info(String.format("Processing: '%s'\n", line)))
.map(line -> line.split("=>"))
.map(array -> array[0] + "=>" + call(array[0], array[1], array[2]))
.collect(joining("\n")) + "\n");
}
private String call(String request, String expectedResource, String expectedContent) {
String[] parts = request.split(" ");
String type = parts[0];
String location = parts[1];
String file = parts[2];
// when
JarPathResourceResolver resolver = new JarPathResourceResolver();
Resource resource = resolver.getResource(file, withResource(type, location));
// then
return String.format("%s=>%s",
getResource(expectedResource, resource),
getContent(expectedContent, resource));
}
@SneakyThrows
private Resource withResource(String type, String location) {
switch (type) {
case "SERVLET":
return new ServletContextResource(new MockServletContext(), location);
case "CLASSPATH":
return new ClassPathResource(location);
case "URL":
return new UrlResource(location);
default:
throw new IllegalArgumentException("Unknown type: " + type);
}
}
private String getResource(String expectedResource, Resource resource) {
if (expectedResource.contains("*") && isMatch(expectedResource, toString(resource))) {
return expectedResource;
}
return toString(resource);
}
private String toString(Resource resource) {
if (resource == null) {
return "NULL";
}
return resource.toString().replaceAll("\\\\", "/");
}
private String getContent(String content, Resource resource) {
if (resource == null) {
return "NULL";
}
String exists = resource.exists() ? "EXISTS" : "NOT EXISTS";
return content.equals("EXISTS") ? exists : load(resource);
}</pre><p style="text-align: justify;">Сложнее для поддержки чем оригинальные юнит тесты, согласен. Но единожды
отладив я практически никогда не лезу в него больше. Тут меня уберегает
другой подход в тестировании - я всегда стараюсь писать тесты на
публичный интерфейс выглядывающий за пределы пакета, а не на конкретный
класс. Я использую все преимущества юнит тестирования но в чуть более
интерационном разрезе. Тогда любой рефакторинг внутри пакета (реализации
его классов, удаление оных, создание новых) не влияет на такие тесты
почти никак.</p><p style="text-align: justify;">Ах да! Надо назвать как-то этот подход. Имя твое - Directive Approvals Testing. <br /></p><p style="text-align: justify;">Все это было написано среди ночи <a href="https://www.youtube.com/channel/UCs7O9sOUQiBGBxaaAguIwig">под приятные слуху колебания воздуха в Patrik Pietschmann</a>. Отдельно хочу отметить эту композицию другого Автора. Такое количества нот я не видел даже у Рахманинова, хотя тут больше гаммы, а у Раманинова тотальный рендом )<br /></p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/ZbBybGKiU0c" title="YouTube video player" width="560"></iframe></center><p style="text-align: justify;">
Кстстати небольшой бонус для тех, кто дочитал до этого места. Пример assertMatch как расширение assertEquals с проверками типа qwertyu == qw*yu. Очень полезно, если надо из expected блока скрыть часть текста, который либо очень недетерминированный либо выдает какие-то особенности dev-окружения закреплять которые в тесте конечно же не стоит. </p><pre> public static boolean isMatch(String expectedPattern, String actual) {<br /> String[] patterns = expectedPattern.split("\\*", -1);<br /><br /> String first = patterns[0];<br /> if (!first.isEmpty()<br /> && !actual.startsWith(first))<br /> {<br /> return false;<br /> }<br /><br /> String last = patterns[patterns.length - 1];<br /> if (patterns.length > 1<br /> && !last.isEmpty()<br /> && !actual.endsWith(last))<br /> {<br /> return false;<br /> }<br /><br /> int pos = 0;<br /> for (String pattern : patterns) {<br /> int index = actual.indexOf(pattern, pos);<br /> if (index < pos) {<br /> return false;<br /> }<br /> pos = index + pattern.length();<br /> }<br /><br /> return true;<br /> }
public static void assertMatch(String pattern, String actual) {
if (!isMatch(pattern, actual)) {
assertEquals(pattern, actual);
}
}</pre>
И тесты для него
<pre> @Test<br /> public void testIsMatch_case1() {<br /> assertEquals(true, isMatch("qwe-asd-*", "qwe-asd-zxc"));<br /> assertEquals(false, isMatch("qwe-Asd-*", "qwe-asd-zxc"));<br /> assertEquals(true, isMatch("*-asd-zxc", "qwe-asd-zxc"));<br /> assertEquals(false, isMatch("*-aSd-zxc", "qwe-asd-zxc"));<br /> assertEquals(true, isMatch("*-asd-*", "qwe-asd-zxc"));<br /> assertEquals(false, isMatch("*-asd-*A", "qwe-asd-zxc"));<br /> assertEquals(true, isMatch("*-*-*", "qwe-asd-zxc"));<br /> assertEquals(false, isMatch("A*-*-*", "qwe-asd-zxc"));<br /> assertEquals(true, isMatch("*", "qwe-asd-zxc"));<br /> assertEquals(false, isMatch("*A", "qwe-asd-zxc"));<br /> assertEquals(true, isMatch("qwe-*-zxc", "qwe-asd-zxc"));<br /> assertEquals(false, isMatch("qwe-A*-zxc", "qwe-asd-zxc"));<br /> assertEquals(true, isMatch("q*-*-zx*-wer", "qwe-asd-zxc-wer"));<br /> assertEquals(false, isMatch("q*-*-Zx*-wer", "qwe-asd-zxc-wer"));<br /><br /> assertEquals(true, isMatch("a*c", "abbbc"));<br /> assertEquals(false, isMatch("a*b", "abbbc"));<br /> assertEquals(true, isMatch("*a*", "banana"));<br /> assertEquals(true, isMatch("*a*", "ananas"));<br /> assertEquals(true, isMatch("**a*", "ananas"));<br /> assertEquals(true, isMatch("*a**", "ananas"));<br /> assertEquals(true, isMatch("**a**", "ananas"));<br /> assertEquals(true, isMatch("*a*b", "ab"));<br /> assertEquals(true, isMatch("*a*b*", "acbabcab"));<br /> assertEquals(true, isMatch("a*", "a"));<br /> assertEquals(true, isMatch("a", "a"));<br /> assertEquals(true, isMatch("*a", "ba"));<br /> assertEquals(true, isMatch("*", ""));<br /> assertEquals(true, isMatch("a*b", "acbabcab"));<br /> assertEquals(true, isMatch("a**b", "acbabcab"));<br /> assertEquals(true, isMatch("a***b", "acbabcab"));<br /> assertEquals(false, isMatch("b*", "ab"));<br /> assertEquals(false, isMatch("b**", "ab"));<br /> assertEquals(false, isMatch("*a", "b"));<br /> assertEquals(false, isMatch("**a", "b"));<br /> assertEquals(false, isMatch("*a*b", "abbc"));<br /> assertEquals(true, isMatch("*a*b*", "acbabc"));<br /> assertEquals(true, isMatch("*a*c", "abbbc"));<br /> assertEquals(true, isMatch("*a*c*", "acbabc"));<br /> }</pre><div style="text-align: justify;">
Приятного аппетита! </div><div style="text-align: justify;"> </div><div style="text-align: justify;">Ах да, этот код/и тесты был сгенерирован вредным ChatGPT, который все никак не хотел слушаться. Ему помог GithubCopilot в двух корнер кейзах. Будь внимателен. LLM моделям снится как они видели как кто-то кодировал когда-то. Именно этот "сон" ты видишь во время общения с ними. В их "коде" и любых утверждениях сосредоточено много ошибок, хотя сказано это с умным видом лица. Я бы сказал 60-80% информации верны - что уже очень-очень неплохо. На остальные % пиши тесты. Много тестов не бывает. О моем опыте промптинга я раскажу в другом посте. </div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">Ах да, и делай Self Code Review перед каждым своим коммитом. Так, на всякий. <br /></div>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-17707342453432180282023-03-03T15:04:00.002+02:002023-03-03T21:19:30.541+02:00Public speaking портфолио <p style="text-align: justify;">Находясь 6 лет в тени своих проектов <a href="https://dojorena.io">https://dojorena.io</a> и <a href="http://codenjoy.com">http://codenjoy.com</a>, обучая других ребят проводить ивенты и отдавая им этот кайф взаимодествия с людьми я ограничивал себя ради более высокой цели. В эти годы не строил бренд собственный, но строил бренд компании, делая и продвигая продукт, который позволял рспостранять инженерный вайб многим. Десятки тысяч инженеров <a href="https://habr.com/ru/company/epam_systems/blog/332486/">прошли через codenjoy-like ивенты</a>. Это была моя миссия и фокус. И это время подходит к концу. Это была моя стратегия и моя большая ошибка - все это время должно было оставаться то, ради чего я весь этот уход в менеджмент затеял - что-то для меня.</p><p style="text-align: justify;">Вчера был на Java ивенте и понял как я соскучился за всем этим организаторским. И первым делом сегодня утром написал ведущему ивента моему коллеге по компании чтобы "побалакать" про возможные будущие активности. Сошлись на том, что поделюсь опытом в прошлом и подумаем как быть. Поделюсь в свойственной мне манере - напишу пост в блог. Так как code reuse, а в будущем будет приятно посмотреть, перечитать.<br /></p><p style="text-align: justify;">Этот пост про былые заслуги. Много всего было. Но мне кажется сделано далеко недостаточно. Буду выкладывать не все ивенты, что были - их было много типичных. Интересно то, какими они были разнообразными. Так же надеюсь, что инженер, такой как я пытливый и зависимый, случайно попав на этот пост вдохновится и сможет разрешить себе отправиться в увлекательное public speaking путешествие. Начинать можно с малого. Так и распостраняется инженерная культура. От экспериментов в коде, через освещение в команде и на все большую сцену. <br /></p><p style="text-align: justify;">Первый опыт был в команде, когда меня менеджер попросил порисерчить очередной какой-то инструмент, типа JUnit (это было лет 15 назад). Я в этом разбирался для команды, перечитывая десятки статей, оставлял интересные 10% практические заметок в виде тезисов на бумажках подсказках, а потом и слайдах. Так я открыл в себе дар пользоваться метаформами для объяснения сложных штук простым языком. И то, что мне это все изучать чтобы потом поделиться очень нравится. Оказывается в XP есть такая отдельная дисциплина - метафора системы. Но это XP я открыл для себя позже. После успеха в своей команде мой опыт пошел по соседним командам. А сменив первую работу на вторую я сразу же подхватил идею моего менеджера делиться внутри команды code review замечаниями, но не peer-to-peer как это обычно принято, а с презентацией и командой в качестве слушателей. </p><p style="text-align: justify;">Тут же с переездом в большой город для меня открылись многочисленные возможности - столько всего происходить, как бы успеть все? <br /></p><ul style="text-align: justify;"><li>Быстро понял, что ходить на конференции в качестве волонтера в разы интереснее и поучительнее, чем в качестве слушателя. Близость к организаторам, навыки в траблшутинге, близость к спикерам, препати (подготовка), афтерпати, афтер-афтерпати, нетворинг зашкаливал. Ходил не только на джава конфы, но и Agile и даже QA (именно на одной из QA конференций я понял, что QA это целый мир, не менее, а часто даже и более глубокий, чем мир DEV инженерии). </li><li>Первая моя <a href="https://www.youtube.com/watch?v=chOOhBg2UhU">рисованная преза</a> на вебинаре, да и вообще первое выступление на большую аудиторию опять таки не будь я волонтером не познакомился бы с организаторами, не пригласили бы они меня сюда. Было страшно и это слышно, но я справился. <br /></li><li>Там же познакомился с коллегой <a href="http://szelenin.blogspot.com/">Сергеем Зелениным</a> и понеслась наша eXtreme Programming коучинг практика. К тому времени я был на рельсах TDD и искал единомышленников в новой компании. Странно, но на всю компанию такой был только один Сережа. Так и подружились. <br /></li><li><a href="http://www.apofig.com/2011/11/tdd.html">Доклады для студентов</a> по вузам Украины об инженерных практиках. </li><li><a href="http://www.apofig.com/2011/11/fest-2011.html">Выступления в школах</a> перед школьниками. </li><li>Десятки семинаров и <a href="http://www.apofig.com/2013/02/tdd.html">авторских тренингов по TDD</a>, для одного из которого с целью геймификации был придуман Tetris Dojo, ставший потом <a href="http://codenjoy.com/">Codenjoy</a> и задавший настроение всему что было после. <br /></li><li>Авторский (наш с Сережей) проект <a href="http://codenjoy.com/?page_id=32">Automated testing Dojo</a> который потом, так же как Codenjoy, ушел в среду автоматизаторов т.к. мы все всегда отдавали в open source. Мы его придумали за пару часов (<a href="https://www.youtube.com/watch?v=Qk11tNN-G78">вот наше демо</a>), а реализовали к ивенту еще через пару недель. <br /></li><li>Выступление на конференциях для автоматизаторов с <a href="https://www.slideshare.net/nunafig/bdd-jbehave-12074696">предложенной нами архитектурой для автоматизации</a> с Selenium WebDriver. </li><li>И много чего еще по мелочам. Все наши с Сережей выходные были распланированы на год вперед или в командировках или в подготовке к ивенту/тренингу/конфе. Это было чудное время полное обмена знаниями и поиске идеальной формы. Презы были скучные и километровые, потом веселые и вдохновляющие, было и LiveCoding, <a href="https://youtu.be/sXlFtgrEZkc">даже рисовали анимацию</a>. </li><li>Все это могло быть на площадке GlobalLogic во время моей работы в тренинг центре компании. Там же я засетапил и поддерживал Moodle LMS с двумя тренингами J2EE и J2SE. Вел группу менторов для кодревью. </li><li>Закрывал роль XP коуча в командах после тренинга по TDD/рефакторингу. <br /></li><li>Вел вместе с преподавателями пару лет c десяток групп студентов на площадке КПИ. </li><li>Организовал <a href="http://j-a-v-a-i-o.blogspot.com/">2 ивента под ключ в Харькове для Java экспертизы</a> с одним мессаджем - нечего слушать долгие заумные доклады, если слушаем то только в формате блиц 20 минут на спикера, а все остальное время нетворкаемся и делаем что-то ручками практически. Конечно же там <a href="https://www.youtube.com/watch?v=2s_YSfVoV8U">был и мой доклад</a>.<br /></li><li>Перенял опыт проведения техтоков Глеба Рабылко их Харьковского офиса в Киевский и провел около 25 техтоков, 5 из которых были наши с Сережей (<a href="https://www.youtube.com/watch?v=Z1WD5Jj9dH4">один из моих первых докладов там</a>, слышно как я волнуюсь). </li><li>А <a href="https://youtu.be/zDd3JivtWTI">вот как мы дурачились</a>, поздравляя ребят в Харькове с их 50м техтоком. Дурачиться - это очень важно. Всякий треш будет происходить от людей хоть и ненамеренно но регулярно, а вот развлекаться - это надо форсить. </li><li>Выступал с от имени компании на стартаперском ивенте с Codenjoy. Занял второе место, наверное из за слов "у нас все хорошо, денег нам не надо". <br /></li><li>С Владимиром Шиманским реализовали корпоративный HackerSpace и проводили уже на нем ряд ивентов, тогда как техтоками продолжила заниматься моя коллега по тренинг центру Татьяна Хряпина. <br /></li></ul><div style="text-align: justify;">Чего только не приходило за почти 4 года работы в GlobalLogic - всему открывался. Потом сменилось руководство и стратегия тренингового центра. Было сложно объяснить человеку из позапрошлого столетия зачем это все и почему оно стоит того, чтобы тратить на это время и деньги. И мы разбежались.<br /></div><div style="text-align: justify;"> </div><div style="text-align: justify;">Отдельно наверное стоит напомнить про опыт организации тусовки "Рекрутёры и программёры" когда на один ивент мы с Линой Шишкиной приглашали вместе представителей этих ролей, чтобы они друг другу объясняли простым языком сложные вещи. Рекрутерам это полезно, чтобы лучше понимать как хайрить ребят потом на рынке, а программистам понимание стратегий построения бренда, вилки зарплат, лайвхаки и так далее. Мне было странно, что никто не додумался до этого формата раньше и потому пришлось его изобрести. 5 встреч (кажется) хватило, чтобы запустить волну подобных инициатив на других площадках после чего продолжать не хотелось, т.к. миссия выполнена. <br /></div><div style="text-align: justify;"><div><p>Далее был опыт построения оффлайн школы в стартапе GoIT. Поначалу это делали вместе с Сергеем Немчинским (сейчас Сережа продвигает свою школу и <a href="https://www.youtube.com/@SergeyNemchinskiy">невероятно успешный ютьюб канал</a>) но в последствии разделились, я ушел в построение программы и в помощи ведении остальных 10 групп а Сережа пошел в сторону своей школы. </p><ul><li>Тут же было много ивентов маркетигновых но на тему Java и около IT (<a href="https://www.youtube.com/watch?v=YfMAL_rkss8&t=1623s">пуньк</a>, <a href="https://www.youtube.com/watch?v=iBnoOas4Dog">пуньк</a>, <a href="https://www.youtube.com/watch?v=0amLfzKSo8g">пуньк</a>, <a href="https://www.youtube.com/watch?v=myC_ENpbPDc">пуньк</a>, <a href="https://www.youtube.com/watch?v=hX-EIROSJRI">пуньк</a>)<br /></li><li>Так и внутри команд для поддержвания духа, например <a href="https://www.youtube.com/watch?v=VwK7DkPp9y4">формат Hackenjoy</a> был придуман как формат Hackaton + Codenjoy. Мы писали игры для уже тогда опенсорсного Codenjoy 48 часов. Так кстати было написано основное большинство ныне существующих игр.</li><li>Были и другие площадки не связанные с GoIT (<a href="https://www.youtube.com/watch?v=1hvfCQzp_nM">пуньк</a> и <a href="https://www.youtube.com/watch?v=SCKg_z7bKxY">пуньк</a>).</li><li>Был и <a href="https://www.youtube.com/watch?v=TY5b7afFHE0">первый опыт проведения вебинара</a> - меня очень волновал онлайн я считал, что оффлайн плохо масштабируется и это одна из причин почему я ушел из GoIT спустя год. </li></ul><p>Затем опыт построения своего стартапа и <a href="https://juja.com.ua/">онлайн школы JuJa</a>. Там я вдоволь насладился онлайном и вебинарами. </p><ul><li>1000 часов моего вещания на ютьюб каналах (<a href="https://www.youtube.com/@apofig2/videos">пуньк</a> и <a href="https://www.youtube.com/@juja4203/videos">пуньк</a> - большая часть видео закрыта, т.к. мы это продавали, но в планах потихоньку сделать это всеобщим достоянием). </li><li>Организация воронки продаж онлайн бизнеса с партнерами. </li><li>Попутно все это организационное с написанием (уже в третий раз) тренинговой программы по java core + frameworks + soft skills. </li><li>А так как аудитория онлайн и доходимость до конца тренинга невелика, придумки всяких поддерживающих инициатив переродившихся в целую душевную не побоюсь этого слова секту свидетелей java ) <br /></li><li>Несколько организованных нашей командой конференций, где так же выступал сам (<a href="https://www.youtube.com/watch?v=UYYK3Vc9_LY&t=1778s">пуньк</a>, <a href="https://www.youtube.com/watch?v=3-pHPa1w7d8">пуньк</a>)</li><li>Много много много продающих вебинаров (<a href="https://www.youtube.com/watch?v=CPvNXhsHSD0">пуньк</a>, <a href="https://www.youtube.com/watch?v=S871vK40ATk">пуньк</a>, <a href="https://www.youtube.com/watch?v=07UXrPn-jXc">пуньк</a>, <a href="https://youtube.com/live/kyGeJFq2YzU">пуньк</a>, <a href="https://youtube.com/live/f1PK9Q7UqLA">пуньк</a>, <a href="https://youtube.com/live/XPQ_Ma__dL8">пуньк</a>, <a href="https://youtube.com/live/9cKC37PigSM">пуньк</a>, <a href="https://youtube.com/live/TCXQAO0rf7A">пуньк</a>, <a href="https://www.youtube.com/watch?v=AG3DuJ-8s4M">пуньк</a>, <a href="https://youtube.com/live/JMNLJEd6qxQ">пуньк</a>, <a href="https://www.youtube.com/watch?v=SIHwTaojWFs">пуньк</a> - их десятки, больше всего я люблю <a href="https://www.youtube.com/watch?v=pY2OKCYTAFY">паттерны головного мозга</a>, потому что дурачился)</li><li>Еще больше Letscode когда я просто включал IDE, микрофон и транслировал по принципу что вижу то и пою в эфир часами (пару примеров: <a href="https://www.youtube.com/watch?v=UYzYgEnHAmg">пуньк</a>, <a href="https://www.youtube.com/watch?v=EGn3ypERVbk&t=0s">пуньк</a>). Этот формат я случайно открыл еще в GoIT придя один раз на лекцию неподготовленный и признав это импровизировал по ходу. На что ребята сказали - так интересно наблюдать за решением проблем им еще небыло. Больше я не готовился ) В последствии вся JuJa построена была на этом знании. Ребята делали сами, а потом смотри как это можно было сделать еще. Плюс кодревью и продающие вебинары, которые так же разрезались на ролики по меньше и вставлялись в LMS в нужное место. <br /></li><li>Были заказы из прошлых компаний на разбор таких штук как Groovy (<a href="https://www.youtube.com/watch?v=QjyHc4tKhMQ">пуньк</a>, <a href="https://www.youtube.com/watch?v=pZHj3U0XKzg">пуньк</a>) и другие комерческие запросы реализованные в виде видосов.</li><li>Были разборы шаблонов проектирования товара ловушки. Обещал 52 осилил половину. Деньги никто не попросил вернуть. Хотя стоила подборка то ли 5 то ли 15$ кажется. (<a href="https://www.youtube.com/watch?v=9JYLt5Zh3Dw">observer</a>, <a href="https://www.youtube.com/watch?v=okQyWhHQDE0">adapter</a>, <a href="https://www.youtube.com/watch?v=SlNR-m3mKaI">strategy</a>, <a href="https://www.youtube.com/watch?v=gV7mOGGkjg0">decorator</a>, <a href="https://www.youtube.com/watch?v=Z9zcZHeYI4w">simple factory</a>, <a href="https://www.youtube.com/watch?v=TovP0CESda0">abstract factory</a>, <a href="https://www.youtube.com/watch?v=p2-sFsC8zu4">factory method</a>, <a href="https://www.youtube.com/watch?v=8EhENGOWndU">composite</a>, <a href="https://www.youtube.com/watch?v=Zxxuw1jSX8A">visitor</a>, <a href="https://www.youtube.com/watch?v=-8w5PhubjgQ">singleton</a>, <a href="https://www.youtube.com/watch?v=GgBsjsrsDQk">command</a>, <a href="https://www.youtube.com/watch?v=Qak6KvI2ds4">chain of responsibility</a>, <a href="https://www.youtube.com/watch?v=W6r4Lh5ZjQE">callback</a>, <a href="https://www.youtube.com/watch?v=xlwAUfrL1xU">caching</a>, <a href="https://www.youtube.com/watch?v=AJxOeBv92Lo">immutable object</a>, <a href="https://www.youtube.com/watch?v=waGeriuR5Ro">null object</a>, <a href="https://www.youtube.com/watch?v=kTFStBwDW1w">builder</a>, <a href="https://www.youtube.com/watch?v=-5HhB3Zdj78">object pool</a>, <a href="https://www.youtube.com/watch?v=ZPf7HUaZnAc">bridge</a>, <a href="https://www.youtube.com/watch?v=w27rT11dGPc">state</a>, <a href="https://www.youtube.com/watch?v=c901axQKhrM">facade</a>, <a href="https://www.youtube.com/watch?v=CU1rumjdDC8">template method</a>, <a href="https://www.youtube.com/watch?v=ORpUJwBtSuM">iterator</a>) ну а времени соответственно потрачено в разы больше. </li><li>Был повторно проведен <a href="https://docs.google.com/document/d/1WatRDAaXwyMRK3ReDE6EN_dWFFJm2YUVh1SB8Ke9lIE/edit">формат Hackenjoy</a>, но уже в онлайне. Игр было написано не так много как в офлайне, где я ходил и помогал всем, но зато был побит рекорд вещания (<a href="https://www.youtube.com/watch?v=W17BKHU9H-Y">пуньк</a>, <a href="https://www.youtube.com/watch?v=zrINVp1RFj4">пуньк</a>, <a href="https://www.youtube.com/watch?v=zarRXhfqlfM">пуньк</a>, <a href="https://www.youtube.com/watch?v=sLl2q-xJhgg">пуньк</a>, <a href="https://www.youtube.com/watch?v=xRytEmeed_E">пуньк</a>, <a href="https://www.youtube.com/watch?v=-L0h_ZWBAKY">пуньк</a>)</li></ul><p>Но так как бы небыло классно, мы подперли с командой потолок и больше не понимали как расти. Бизнеса
не получилось через 4 года устали и разругались с партнерами и больше
не продавали ничего. До сих пор плачу за хостинги, не поднимается рука убить контент. Но опыт был классный.</p><p>Так как параллельно с этим всем я проводил активно Codenjoy ивенты на всевозможных площадках. Вначале это был тетрис (<a href="http://codenjoy.com/?p=15">пуньк</a>, <a href="http://codenjoy.com/?p=19">пуньк</a>, <a href="http://codenjoy.com/?p=237">пуньк</a>, <a href="http://codenjoy.com/?p=275">пуньк</a>, <a href="http://codenjoy.com/?p=396">пуньк</a>). Потом появилась змейка (<a href="http://codenjoy.com/?p=360">пуньк</a>, <a href="http://codenjoy.com/?p=389">пуньк</a>). Дальше появился бомбермен (<a href="http://codenjoy.com/?p=441">пуньк</a>, <a href="http://codenjoy.com/?p=543">пуньк</a>, <a href="http://codenjoy.com/?p=660">пуньк</a>, <a href="http://codenjoy.com/?p=639">пуньк</a>, <a href="http://codenjoy.com/?p=678">пуньк</a>). Battlecity (<a href="https://www.facebook.com/media/set/?set=a.665122166872751.1073741843.160943713957268&type=1">пуньк</a>). В общем понеслась... Так вот нас приглашали разные компании и в какой-то момент это сделала та самая, где я оставил 6 лет. Все ради нескольких сотен ивентов и десятков тысяч инженеров прикоснувшихся к этому чудному формату. Ивенты вначале пробовал проводить сам, а потом пришлось разивать движение Сенсеев, которые уже сами проводили ивенты на локациях. Так же на 4й год начал появляться продукт <a href="https://dojorena.io">https://dojorena.io</a>, как наш ответ на все возникающие запросы организаторов. Как выглядят dojo ивенты сейчас? (<a href="https://www.youtube.com/watch?v=RirbiLFFLjw">пуньк</a>, <a href="https://www.youtube.com/watch?v=9L43XGDioWk&t=4953s">пуньк</a>, <a href="https://www.youtube.com/watch?v=KFB7y2h7EFM&t=1798s">пуньк</a>).</p><p>Вот такая вот история. Я не буду рассказывать в ней про мои кодинг активности на проектах, там было много и интересного-вдохновляющего и супер-скучного. Эти переживания (от увлечения до поытки сбежать) собственно и побуждали делиться потом с коллегами на всевозможных площадках. Но точно знаю - со скучным лицом ничего выдающегося не придумаешь. Дурачьтесь. Нудить успеется.<br /></p></div></div>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-60544011699338476282023-02-11T20:29:00.002+02:002023-02-12T12:42:05.237+02:00Как преобразовать десятичную дробь в обыкновенную<p>Наткнулся на ролик, не мог пройти мимо. Люблю математический мэджик. Вот он.</p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/N-5K1OfKKEw" title="YouTube video player" width="560"></iframe></center>
<p style="text-align: justify;">Навык совершенно бесполезный, но задачка для ума интересная. Мне захотелось увидеть универсальную закономерность. Ведь речь в ролике о простом периоде 0.(6) а что если период будет 0.(123), или число посложнее 0.123(345), или даже 12.345(678). Сказано сделано. Листочик A4, ручка, 10 минут и готово. Дальше привожу несколько примеров, дабы уловить суть. Тут ABCDE - это цифры в числе.<br /></p><p><span class="style-scope yt-formatted-string" dir="auto">0.AB(CDE) = (ABCDE - AB) / (100000 - 100)</span><span class="style-scope yt-formatted-string" dir="auto">
</span><span class="style-scope yt-formatted-string" dir="auto"><br />0.AB(CD) = (ABCD - AB) / (10000 - 100)</span><span class="style-scope yt-formatted-string" dir="auto">
</span><span class="style-scope yt-formatted-string" dir="auto"><br />0.A(BC) = (ABC - A) / (1000 - 10)</span><span class="style-scope yt-formatted-string" dir="auto">
</span><span class="style-scope yt-formatted-string" dir="auto"><br />0.(ABC) = (ABC - 0) / (1000 - 1)</span><span class="style-scope yt-formatted-string" dir="auto">
</span><span class="style-scope yt-formatted-string" dir="auto"><br />0.(AB) = (AB - 0) / (100 - 1)</span><span class="style-scope yt-formatted-string" dir="auto">
</span><span class="style-scope yt-formatted-string" dir="auto"><br />0.(А) = (А - 0) / (10 - 1)<br /></span></p><p style="text-align: justify;"><span class="style-scope yt-formatted-string" dir="auto">Как понять сколько нолей в числах в разнице в знаменателе? Оно равно количеству цифер в соответсвующих числах в разнице в числителе. <br /></span></p><p style="text-align: justify;"><span class="style-scope yt-formatted-string" dir="auto">С целой частью в знаменателе появляется еще один множитель (от чего нули в знаменателе можно посокращать), нолей в котором столько же сколько цифер в целой части:<br /></span></p><p><span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto">A.BC(DEF) = 10*(ABCDEF</span><span class="style-scope yt-formatted-string" dir="auto"> - ABC) / (1000000 - 1000)<span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto"><br />ABC.D(EF) = 1000*(ABCDEF</span><span class="style-scope yt-formatted-string" dir="auto"> - ABCD) / (1000000 - 10000)</span></span><br /><span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto">AB.CDE(F) = 100*(ABCDEF</span><span class="style-scope yt-formatted-string" dir="auto"> - ABCDE) / (1000000 - 100000)</span></p><p style="text-align: justify;"><span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto"></span><span class="style-scope yt-formatted-string" dir="auto"> </span><span class="style-scope yt-formatted-string" dir="auto">Это не формула, а наглядная схема. Математическая формула будет сложнее и наглядность пропадет. Цель увидеть суть. </span></p><p style="text-align: justify;"><span class="style-scope yt-formatted-string" dir="auto">Но ладно, вот притянутая за уши формула:</span></p><p style="text-align: justify;"><span class="style-scope yt-formatted-string" dir="auto">[abc.def(ghi)] = M^P(x)*(y - z</span><span class="style-scope yt-formatted-string" dir="auto">) / </span><span class="style-scope yt-formatted-string" dir="auto">(</span><span class="style-scope yt-formatted-string" dir="auto">M</span><span class="style-scope yt-formatted-string" dir="auto">^</span><span class="style-scope yt-formatted-string" dir="auto">P(y) - </span><span class="style-scope yt-formatted-string" dir="auto">M</span><span class="style-scope yt-formatted-string" dir="auto">^</span><span class="style-scope yt-formatted-string" dir="auto">P(z)) <br />где M - основание системы счисления, в привычном мире равная 10; <br />P(x) - количество цифер в числе q, например P(123456) = 6, а 10^</span><span class="style-scope yt-formatted-string" dir="auto">P(123456)</span> = 1000000;<span class="style-scope yt-formatted-string" dir="auto"><br />x - целая часть десятичной дроби [abc];<br />y - целое </span><span class="style-scope yt-formatted-string" dir="auto">число составленное из подряд идущих цифер</span><span class="style-scope yt-formatted-string" dir="auto"> исходной десятичной дроби: </span><span class="style-scope yt-formatted-string" dir="auto">включая целую часть, часть до периода и самого периода [abcdefghi];<br />z - целое число составленное из подряд идущих цифер</span><span class="style-scope yt-formatted-string" dir="auto"> исходной десятичной дроби без периода [</span><span class="style-scope yt-formatted-string" dir="auto">abcdef</span><span class="style-scope yt-formatted-string" dir="auto">].</span></p><p style="text-align: justify;"><span class="style-scope yt-formatted-string" dir="auto">Математики перекрестятся )</span></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-17188799818101614292023-01-30T11:47:00.003+02:002023-01-30T11:47:53.555+02:00Свободы воли не существует<p> Пару лет назад я попал на это видео и мой мир перевернулся. <br /></p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/IysBQwPur3A" title="YouTube video player" width="560"></iframe></center><p>И в этом я думаю главная наебка всего человечества.<br /></p><p style="text-align: justify;">Как программист в мире детерминированных кремниевых полупроводников могу сказать, что все, что я программировал - всегда реагировало на внешние обстоятельства, но так как того хотел я. Я выбирал это поведение - моя программа никогда не делала этого выбора. Она могла выбрать только из того, что я позволил выбирать. Да, я мог добавить немного энтропии в это поведение - скажем воспользовашись великим new Random().nextInt(); Но это так же воздействие внешней среды (для моей программы). Особенно, если это рендом взятый из <a href="http://random.org">random.org</a>. </p><p style="text-align: justify;">Другой факт из жизни. Дети Маугли. Дети по страной случайности выжившие благодаря заботе животных. Стали ли они в окружении родителей-животных сочинять музыку, писать стихи, лепить из глины, писать картины, размышлять о вечном, выдвигать гипотезы и опровергать их? Нет. Если повезет и такие детки до 3х лет были с родителями-людьми, то их можно будет хоть как-то адаптировать к жизни. Если нет - человеком разумным им не стать никак. </p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/1eGGkaEgxoU" title="YouTube video player" width="560"></iframe></center><p style="text-align: justify;">Итого само наличие мощной аппаратнрой части само по себе не приведет к тому, что там появятся привычные нашему виду смыслы. Это как супер геймерский компьютер неведомым образом попавший к старой бабушке бухгалтеру, которая на нем только Excel для отчетов да пасьянс Косынку гоняет. И никто не против. Не появится на этом компе никакая новенькая игрушка без участия заботливого внучка, внезапно зачастившего к бабушке. </p><p style="text-align: justify;">Внутри мозга пока что не найдено штук принимающих решение. Все реакции - реакции на внешние раздражители. Любое новое раздражение сенсоров оставляет навсегда след, который поменяет способ реагирования в будущем. Получается весь накопленный опыт - это то, как не могла не действовать нейросетка на раздражители, которые не выбирала. Где тут я? Где тут моя свобода воли? </p><p style="text-align: justify;">Я вижу сознание как инструмент, который безусловно помогает анализировать и собирать дополнительную информацию о мире, но в его алгоритмах нет никакого "это я выбрал" - есть только "ой, что-то делается - кажется это я". Сознание начало появляться не сразу. Первый год - осознание границ своего тела. До 3х лет - самосознание. </p><p style="text-align: justify;">Представь себя маленьким бубусиком, накормленным, довольно играющимся своими руками. О, это что такое? Оно двигается! Ой, кажется это я двигаю. Прикольно как. Проблема только в том, что моторная кора отрабатывает на 500 мс раньше, чем это осознает сознание. И так в любом действии. Вначале реакция, а потом осознание. Сознанию было бы очень скучно просто пассивно наблюдать. Вот оно и приписало себе в какой-то момент времени что это оно решает и всем рулит. Хрен там!</p><p style="text-align: justify;">Сознание, это как.... Представь себе робот пылесос. У него много программ. Как собирать пыль (елочкой, зигзагом, по кругу). Как находить док станцию. Как заряжаться. Как сигнализировать о переполнении мусором. И так далее. И есть на этом труженнике пылесосе - светодиодик, который умеет моргать разноцветными огоньками. И он не просто весело и рендомно моргает - так он сообщает о своих статусах владельцу. Модуль отвечающий за сбор информации и интерптерацию ее в череду разных огоньков - достаточно вездесущ. Этому модулю надо собирать информацию из разных других модулей о том, как они работают (или не работают). Этот модуль и есть наше сознание. Много знает. Но все постфактум. Наблюдая объясняет. Добавляя немного обратной связии с выходов на вход. </p><p style="text-align: justify;"> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7JwTr3EsVxy5VJr_ERaK6z315hbdp9npS0a_xTJcp3uCrEAFlxuARFm2Xa-T0C0jO-UkFvAFbfetV7tjHzdeDcZQbsyrUAR8OLB2UmKJr8hT3jLo2Ozwd3jFS-0B4zXpqaOD668HqQ88rOskmQnbPcMKzaWwTMcC9gJW2fYnRMqCoJYqTdac-VFXbdg/s800/WtDesiz1uh8CKVNyiJbpz1yT4ymEC9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="580" data-original-width="800" height="464" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7JwTr3EsVxy5VJr_ERaK6z315hbdp9npS0a_xTJcp3uCrEAFlxuARFm2Xa-T0C0jO-UkFvAFbfetV7tjHzdeDcZQbsyrUAR8OLB2UmKJr8hT3jLo2Ozwd3jFS-0B4zXpqaOD668HqQ88rOskmQnbPcMKzaWwTMcC9gJW2fYnRMqCoJYqTdac-VFXbdg/w640-h464/WtDesiz1uh8CKVNyiJbpz1yT4ymEC9.png" width="640" /></a></div><p></p><p style="text-align: justify;">Как вот усилитель, если его выход направить осторожно на вход - он возбуждается и превращается в генератор сигналов любой формы, в зависимости от того как соединен выход со входом. Слышал жеж этот писк (возможно с эх-эх-эхо-эхо-эхом), когда микрофон близко поднести к динамику? Это оно и есть. Эта связь дает возможность нейросети обучаться самой от себя, даже если внешние раздражители приутихли и перестали удивлять. Наверное потому у сознания так же есть доступ к (им же) категоризированному прошлому и гипотетическому будущему. </p><p style="text-align: justify;">Это приводит к решениям второго порядка. Не осонованным на стимулах полученных здесь и сейчас. Так говорят о штуках типа морали, ценностей, принципах, культуре... Но это всего лишь самовозбужденный усилитель, который издает определенный звук (сложный, красивый) но все же такой, который он не мог не создавать при всех тех условиях, которые с ним случались.</p><p style="text-align: justify;">И да, я точно знаю, что отмотай мою нейросеть в прошлое, давай ей все те же стимулы-раздражения, она точно придет в совсем другое состояние. Задача о трех телах показычает, как сильно что-то незначительное влияет на будущее. А мы живем все же в мире квантовой механики. Роботы-труженики белки внутри наших заводов-клеток нашего тела, очень-очень близки к квантовым процессам т.к. являются молекулами. А процессы эти квантовые уже недетерменированы аж никак. И эта аппаратная часть этого мира - это снова не про мой выбор. </p><center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/Ee9yBJ6C8u4" title="YouTube video player" width="560"></iframe></center> <p></p><p style="text-align: justify;">Я не выбирал ни время, когда я рожден. Ни место где я родился. Ни семью в которой прошли самых важжных первых три года становления моей нейросети. Я не выбирал ни тот опыт, который со мной случался после. Ни то как я реагировал на него. Ни то к каким смыслам я приходил в процессе переосмысления пережитого. Ни то как я пользовался этим знанием, чтобы преодолевать некоторые будущие атоматические реакции. Выбирал ли я людей, с которыми встречаюсь? Выбирал ли я квесты, которые мне приподносила жизнь? То, чему меня наполняли прочитанные мной книги? Выбирал ли я сами книги или просто реагировал на их содержимое в поиске того самого отклика моей нейросети? Выбирал ли я утром написать этот пост? Да я долго планировал его. Но я ли планировал? Или планирование происходило. Что я выбирал? </p><p style="text-align: justify;">Мы много выбираем - это факт, но из вариантов, которые выбрать не можем. Если бы это было не так, тогда курильщики выбирали бы не курить. Алкоголики - не пить. Люди в депрессии выбрали бы не переживать боль. Бедные люди выбирали бы богатеть. Неумелые - образовываться. Больные - лечиться и выздоравливать. </p><p style="text-align: justify;">Почему некогда богатый человек, объявивший себя банкротом и потерявший все свое состояние вскоре возвращает все свое состояние и даже преумножает его? Почему то же не может выбрать каждый бедный человек? Почему не каждый ребенок из бедной семьи вырывается из лап безысходности и громко заявляет этому миру своими достижениями? Потому, что никто нифига не выбирает на самом деле. </p><p style="text-align: justify;">Все просто плывут по течению. Какие-то нейросети получают больше плюшек. Какие-то меньше. Это несправедливо, но это так. Но все они находятся в постоянном обмене. Богатые без бедных не возможны. Как нейроны в мозге. Каждый из них - целый завод, принимающий, обрабатывающий и передающий дальше информацию. Соединенный с множеством других нейронов. Сам по себе он вроде как ничего и не значит - капля в море. Но убери его и результат будет кардинально иным. </p><p style="text-align: justify;">Представь теперь на секунду. В мозге человека 100 миллиардов нейронов. В мире ныне живущих 8 миллиардов, но когда-либо жило около 100 миллиардов. Ведь мы не изобретаем колесо с кажим новым поколением - мы пользуемся наработками ранее живших. Потому в моем понимании - они все живы. Их опыт закодирован в синапсах нашей культуры, науки, религии. Каждый человек - это нейрон. Все связаны друг с другом через несколько рукопожатий. Все обмениваются знанием, раздражают друга. О чем думает эта большая нейросеть? Каковы мысли у живого существа с 100 000 000 000 х 100 000 000 000 нейронов? <br /></p><p style="text-align: justify;"></p><center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/gLUIDMI7B4w" title="YouTube video player" width="560"></iframe></center><p></p><p style="text-align: justify;">Бери попкорн. Смотри кино. Все происходит прямо сейчас. <br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-58257483729048457322023-01-30T10:02:00.005+02:002023-01-30T10:21:21.553+02:00Кризис среднего возраста - время для рефакторинга смыслов<div style="text-align: justify;">У любого студента есть момент Х, когда он/она понимает, что если отложит сегодня - до сессии не успеет. Если про экзамены, то заучивание вполне можно отложить на самую последнюю ночь. Если про говорить диплом - мой личный рекорд 2.5 недели с нуля под ключ. Но после сессии будут каникулы и очередной семестр беззаботности. А после диплома будет первая работа, потом вторая, третья. Столько всего еще предстоит. </div><p style="text-align: justify;">С жизнью в целом все сильно иначе. Я не верю без доказательств. Как бы ни хотелось, чтобы как в игре - увидел те самые The End и запустил игрушку с самого начала. Я знаю, что ничего не будет после смерти - для меня не будет, а вот мир в целом этого почти не заметит. Я знаю зачем были придуманы религии. Я знаю, как психика блокирует рефлексию на тему "я когда-то тоже умру". Так вот кризис среднего возраста для меня - это то самое время Х, когда понимаешь - если не сейчас, то уже точно не успеешь. </p><p style="text-align: justify;">Где-то на половине жизненного пути случается понимание, что если все делать так как дальше - тебе понадобится лет 400, чтобы успеть сделать все, о чем мечтал. А опыт знавших тебя людей, которые все чаще начинают уходить, фидбеки тела которое иногда начинает подводить, и та самая внезапность смертности - хором поют, не будет так. И нет ничего предложить в ответ, кроме как действие. </p><p style="text-align: justify;">Бессмысленное действие. Поскольку все кого я знал и для кого-то старался когда-либо умрут. Все их труды и наработки коогда-то превратятся в труху или сгорят. Сгорит наш дом - планета Земля. Сгорит в очаге нашего теплого Солнца. Ведь оно тоже стареет и со временем превратится в красного гиганта. Переселение в другую звездную систему ничего не решит. Вселенная расширяется. И превратится когда-нибудь в однородный суп из ничего. Даже черные дыры, жадно поглощающие материю и информацию так же испарятся. Ну или ладно, не испаряются - тогда это роботы пылесосы, которые отпылесосят все вокруг до полной чистоты. Смысла нет. </p><p style="text-align: justify;">Но это будет без меня. Очеь-очень далеко в будущем. А сейчас я часть этой Вселенной, сотканный из материала рожденного внутри звезд во время их кончины, что пытается познать эту самую Вселенную. Как? Очень лениво. Очень-очень медленно. Неспешно. Так, будь-то бы впереди есть 400 лет. По какой-то дурацкой привычке. С мыслью, что всем рулю. А осталось-то лет 40 всего. Страшно. И жалко себя, любимого. И хочется как-то действовать. Но осознание парализует.<br /></p><p style="text-align: justify;">Хотя и бездействие - так же действие. Все произойдет так или иначе. Но есть шанс подглядеть что-то еще. Прикоснуться к тому, от чего мурахи. Пусть не успеть стать профессионалом в этом. Но хоть прикоснуться. К тому, что всегда откладывал на потом. Очень мало времени попробовать все. Блин, как мало.</p><p style="text-align: justify;">Все будет хорошо. <br />А потом мы умрем. </p><p style="text-align: justify;">P.S. Так я обычно заканчиваю какую-то переживательную мысль в компании собеседника. Но сегодня я закончу моим любимым переводом стиха Омара Хайяма. </p><p style="text-align: justify;"></p><blockquote><p style="text-align: justify;"><i>Кто понял жизнь тот больше не спешит,<br />
Смакует каждый миг и наблюдает,<br />
Как спит ребёнок, молится старик,<br />
Как дождь идёт и как снежинки тают.</i></p><p style="text-align: justify;"><i>В обыкновенном видит красоту,<br />
В запутанном простейшее решенье,<br />
Он знает, как осуществить мечту,<br />
Он любит жизнь и верит в воскресенье, <br /></i></p><p style="text-align: justify;"><i>Он понял то, что счастье не в деньгах,<br />
И их количество от горя не спасет,<br />
Но кто живёт с синицею в руках,<br />
Свою жар-птицу точно не найдет</i></p><p style="text-align: justify;"><i>
Кто понял жизнь, тот понял суть вещей,<br />
Что совершенней жизни только смерть,<br />
Что знать, не удивляясь, пострашней,<br />
Чем что-нибудь не знать и не уметь.</i></p></blockquote><p style="text-align: justify;"></p><p style="text-align: justify;">Я смотрю на свой завтрак. Раньше я его бы так быстро проглотил бы, как закинул бы поленья в топку холодной печи, лишь бы быстрее согреться. Проглотить и побежать дальше жить жизнь. Но так же были в моей жизни вкусняшки, которые настолько вкусны - что хочется их смаковать очень долго. Растирая языком по нёбу. Махонькими кусочками. Пусть так будет со всем. Концентрированно. Неспешно. И пусть времени хватит на то, на что хватит. </p><p style="text-align: justify;">Мой завтрак - я иду перетирать тебя об нёбо. </p><p style="text-align: justify;">Кстати, как думаешь - почему оно ребристое? Вики пишет, что "Поперечные складки нёба у человека являются рудиментами нёбных складок у животных, которые участвуют в перетирании пищи". Я это всегда знал. Там же <a href="https://ru.wikipedia.org/wiki/%D0%9D%D1%91%D0%B1%D0%BE">на вики</a> можно увидеть нёбо бегемота, если вдруг надо отвлечься.<br /></p><p style="text-align: justify;">Удивляй... ся!<br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-66732528172601362592023-01-25T14:31:00.003+02:002023-01-25T14:31:26.562+02:00Моя версия эмулятора ЛИКа - или почему инженеры делают то, что делают<div><p style="text-align: justify;">Мой герой - инженер. Именно ботаны двигают этот мир вперед. 88й год, бытовой самодельный компьютер "Специалист". Небыло не то что "войти в IT" небыло вообще никаких "интернетов" (разработка той самой Всемирной Паутины Веб, или сокращенно WWW, началось годом спустя в 89м). Небыло никакой какой-либо комерческой выгоды. Но у уважающего себя инженера дома был бытовой компьютер (БК), собранный собственноручно по схемам из журналов Радио. Потому что мог. Спасибо Папе, что привил мне любовь к этому делу. (на видео не мой Папа, но мой был таким же чудным).</p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/0TTXVMMWlbU" title="YouTube video player" width="560"></iframe></center>
<p style="text-align: justify;">Поделюсь своей историей, которую хочется продолжать. Если я уйду когда-нибудь из IT, то из кодинга оставлю себе мой ЛИК и все, что с ним связано. Сейчас я пишу эмулятор, делаю реверс инжиниринг реального компа, который совсем недавно попал мне в руки. Делаю это все со скоростью улитки, смакуя каждый момент. Как та вкусняшка, которую хочется есть самой маленькой ложечкой, растирая языком по небу каждый новый кусочек. Конечно на любой проект нужно время, а его как всегда мало. Но даже если бы его было в избытке, мой первый компьютер - штука особенная. Это то, что возвращает магию.</p>
<p style="text-align: justify;">Первым компьютером у меня был Синклер. И конечно же Синклер этот использовался для игр. Их было очень много. Даже в эпоху Дэнди у меня (и всех до кого я мог дотянуться) небыло столько картриджей. Ко мне домой приходли мои одноклассники. Мы все первоклашки. Какая домашка? Мы играли каждый день после уроков. Я был крут. У меня дома было что-то, что ни у кого небыло. Напомню, это были 90е. 1992 год, кажется. И конечно же я успешно забил на учебу. Оценки упали. А Синклер был продан. Травмирующий опыт. Была игрушка и ее забрали. Не делайте так родители, никогда. </p>
<p style="text-align: justify;">Вторым моим компьютером был ЛИК. Производства Черновицкого завода Электронмаш. Чернобелый. С маленьким пузатым экраном. Тоже с записью на магнитофон (чуть позже покажу как звучала эта музыка байтов). И конечно же первым делом этот бытовой (как его называли) компьютер использовался мной для игр. Папа по выходным осваивал встроенный Бейсик. Первая его программа - нарисованный герб Украины. А я игрался. Игрался, пока качество кассетной записи не худшилось настолько, что воспроизвести игры больше не получалось. Это было всеобщее горе.</p>
<p style="text-align: justify;">Но ничего не поделать - компьютер есть, потребность ковырять его никуда не делась. Мы с Папой стали изучать Бейсик вместе. Очень скоро я превзошел Папу и пошел дальше. Началось все из того, что мне захотелось собрать коллекцию всех runtime-ошибок существующих в Бейсике. Но не все из них было понятно как достать. Только краткое описание в руководстве по эксплуатации давали хоть какие-то зацепки. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKdIO8eBtI46u6VWptVRVpz7-BuiVkgbFfuV7bXib8_oehEsuyZlN0Lzy1Xppbjto1ZSGGLJTpNp_zj2XnTTrJJj609KYq1OPL1LaGF80NIjXZetXR20g2We50YMWXc96dgiW2rFiDZULN1SlVanadiMmFSxwDxW0CJFEpULgpah1VUI1zqWHitH4-HQ/s894/%D0%9E%D1%88%D0%B8%D0%B1%D0%BA%D0%B8.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="597" data-original-width="894" height="429" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKdIO8eBtI46u6VWptVRVpz7-BuiVkgbFfuV7bXib8_oehEsuyZlN0Lzy1Xppbjto1ZSGGLJTpNp_zj2XnTTrJJj609KYq1OPL1LaGF80NIjXZetXR20g2We50YMWXc96dgiW2rFiDZULN1SlVanadiMmFSxwDxW0CJFEpULgpah1VUI1zqWHitH4-HQ/w640-h429/%D0%9E%D1%88%D0%B8%D0%B1%D0%BA%D0%B8.jpg" width="640" /></a></div><br /></div><div style="text-align: justify;">Как говорится: нифига не понятно, но очень интересно. Одни ошибки попадались чаще всего - 02, например. Другие все никак не поддавались. Так я изучил почти все функции языка и многие corner-кейзы их использования.</div><p style="text-align: justify;"></p><p style="text-align: justify;">Оставалось всего несколько команд в бейсике, которые по моему были самые магические и непостижимые. Просматривая исходные коды игр на бейсике (а это был мой любимый квест, ведь столько всего можно узнать в чужом исходном коде работающей игры) я часто натыкался на эти команды и понимал, что за ними кроется все самое интересное - звук, красивая растровая графика, более удобный ввод/вывод. Они мне открылись не сразу. Все что я мог - менять циферки аргументов и смотреть как компу отшибает мозги, либо что-то происходит необычное. Использовать это повторно нельзя было. Magic одним словом. Но очень любопытно. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCJaCFSAYApZ8R0-NULgSLBIhZcsal0ZymyxeTdub9P-ET2ww3_G5z7xBkJSYhmP3JEOGxtZG5xFShJo8EXRtQPcZiSmjxB8-HfZTET_2xsR9UGPmQX2bAOlv7ecbApGi0WvwibHSF5926-YlekS_C82fTwYKVy-T_qFWOKkIpaedvl0wNTjJCds-XTA/s912/Peek%20poke%20usr.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="719" data-original-width="912" height="504" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCJaCFSAYApZ8R0-NULgSLBIhZcsal0ZymyxeTdub9P-ET2ww3_G5z7xBkJSYhmP3JEOGxtZG5xFShJo8EXRtQPcZiSmjxB8-HfZTET_2xsR9UGPmQX2bAOlv7ecbApGi0WvwibHSF5926-YlekS_C82fTwYKVy-T_qFWOKkIpaedvl0wNTjJCds-XTA/w640-h504/Peek%20poke%20usr.png" width="640" /></a></div><p></p><p style="text-align: justify;">Потом поломался и Бейсик. Он был записан в ПЗУ. И что-то там видимо перетерлось, т.к. контрольные суммы стали выдавать другое значение - что говорило мне о том, что ПЗУ накрылась. Где накрылась не понятно. Что с этим делать так же не понятно. Больно.<br /></p><p style="text-align: justify;">Ковырять чесалось. А потому мое внимание привлек раздел руководства по экплуатации, которое про встроенный ассемблер. Я понимал, что Бейсик - это интерпретатор языка более высокого уровня в машинный код. Я понимал, что процессор выполняет инструкции, которые записаны байтами в оперативной или постоянной памяти. Часть этих байт - инструкции, часть собственно данные. Время пришло. </p><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJYHtSyFxnV0fe2fhhL27YRzvykyzA2bRL5rRho8_Y6m90-0bjHKOoJypsJSgKMnZ1jePChWcxOugc_uISSF7x25Xk9M6hj1TO5n8JVJTAkjGhlh_ZjNNc-vPrmLIb7v2YqJh-Gn0__c9vib7h605RK0gj5sffqPX4-y4miubnWhEO6yRbRSO-q6Y4Mg/s844/asm.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="333" data-original-width="844" height="253" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJYHtSyFxnV0fe2fhhL27YRzvykyzA2bRL5rRho8_Y6m90-0bjHKOoJypsJSgKMnZ1jePChWcxOugc_uISSF7x25Xk9M6hj1TO5n8JVJTAkjGhlh_ZjNNc-vPrmLIb7v2YqJh-Gn0__c9vib7h605RK0gj5sffqPX4-y4miubnWhEO6yRbRSO-q6Y4Mg/w640-h253/asm.png" width="640" /></a></div><div style="text-align: justify;">В понимании мне помогла директива дизассемблирования. Она магическое шестнадцатеричное число приводила в чуть более осмысленный код. Но по прежнему этот код ни о чем не говорящий. Создать их каталог была моя задача. С этого начинается любое исследование. Там же я заметил первые закономерности. Старший байт, младший байт. Регистры. Запись/чтение в память. Руководство давало не очень подробное, но все же какое-то объяснение. <br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlSamoQdhGpZcLkffNGZJ6RPqxNKEilrAunehlRcKQ-BJTVkecMDIuq8uRgJ7U6Y7cUqXPA2Wr5O9mqlz-Pma3ymUirMb1QTJ5K1jbuGrnvEQ_oLZTzLaCt-5uzJpZcI38kCIJW6akWe9fo-a8lf3Lc1LXqHH8PsE8VMpPh-j6weFFNLbJHQYtBE1yLw/s590/instr.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="516" data-original-width="590" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlSamoQdhGpZcLkffNGZJ6RPqxNKEilrAunehlRcKQ-BJTVkecMDIuq8uRgJ7U6Y7cUqXPA2Wr5O9mqlz-Pma3ymUirMb1QTJ5K1jbuGrnvEQ_oLZTzLaCt-5uzJpZcI38kCIJW6akWe9fo-a8lf3Lc1LXqHH8PsE8VMpPh-j6weFFNLbJHQYtBE1yLw/w400-h350/instr.png" width="400" /></a></div><div style="text-align: justify;">Если ты когда-то смотрел/а фильм Прибытие. Все было именно так. Встреча с пришельцами инопланетянами говорящими на непонятном языке. Они пришли что-то мне дать. Что именно - я должен разгадать. Каждый день я просыпался и все, что хотел - еще немного поговорить с ними. Я делал это весь день и пол ночи. Отвлекаясь только на еду и сон. Это был 5й класс школы на секунду. <br /></div><div><p></p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/5Dyoa99FZiw" title="YouTube video player" width="560"></iframe></center>
<p>Фильм кстати очень рекомендую для многократного пересмотра. <br /></p>
<p style="text-align: justify;">Разбирая команду за командой, вскоре не осталось ни одной команды, которую я бы не понимал. Ассемблер оказался куда проще и грандиознее, чем бейсик. Я писал свои программы. Отлаживал их. Писал инструменты для написания программ и их отлаживания. Отлаживал эти инструменты. Обрастал кодом. Как положено. <br /></p>
<p style="text-align: justify;">А потом случился инсайт. Я заметил, что иногда, подкручивая магнитную головку кассетного магнитофона звук становился чуточку лучше и некоторые программы все же удавалось считать с кассеты. А что если взять этот звук с магнитофона и не подавать его на вход компьютера как обычно, а пропустить через усилитель (я на нем слушал музыку) там повысить уровень (максимально зашкалив его) и снять с линейного выхода уже усилителя сигнал, направив его к компу. Пяльник к тому времени я уже освоил хорошо. Спаял еще один провод и запустил опытную установку. </p>
<p style="text-align: justify;">Первая игра конечно же Клад. И она считалась. Вторая игра. Считалась. Третья. Считалась. Все, что я не пробовал - все считалось. И работало. С любой из 20 кассет, в любом месте что ни брал - все считывалось без ошибков. Напомню, к этому времени на кассетах почти ничего не читалось, т.к. их качество оказалось ужасное и мы с Папой пропустили момент когда надо было переписать на новые. Конечно же я не мог дождаться возвращения Папы с работы. Он плакал. То ли от гордости за меня, то ли за вторую жизнь всем нашим наработкам. Он так и не сказал, но где-то тут я четко понимал, что уже взрослый я, а ребенок он. Мы всю ночь переписывали все игры на новые кассеты. Все восстановили. С тех пор я знаю. Как бы безнадежным не казалось дело - пока есть с чем работать, информацию можно восстановить. </p>
<p style="text-align: justify;">Так как у меня появились новые-старые игры. Я начал их изучать. Это было не просто. Но постепенно они открывали свои шестнадцатеричные секреты. Подпрограммы Загрузчика. Подпрограммы Монитора. Разные полезные штуки вычлененные из игр. Самая моя большая гордость - это додебажиться до той процедуры, которая отвечает за звучание кринжового голоса говорящего "BUDY" во время заставки одноименной игры. Удаление всего лишнего, так что остается всего 25-30 байт кода. И реализация на основе этого кода диктофона. Мой комп превратился в диктофон, где секунд 5-7 звука записывалось из микрофона в 48 килобайт оперативной памяти. А потом это все RAW воспроизводилось на динамик. И затем снова перезаписывалось с микрофона. Качество конечно было неахти, но все было слышно. После этого я понял - что могу все. Вот прям все. <br /></p>
<p style="text-align: justify;">Затем мой комп поломался вовсе. И я остался с паяльником. Погрузился в мир логических элементов и строил разные примитивные схемки. Читал про моделирование компьютера подобного моему в книге. Паял много. Запах канифоли в носу как сейчас. Светил светодиодами. Обжигал пальцы. Пока не увлекся Химией в 9м классе. </p>
<p style="text-align: justify;">Конечно я хотел вернуться в эти воспоминания. Я долго искал возможности приобрести компьютер ЛИК. А тем временем я пытался по фоткам плат, доступным схемам <a href="https://zx-pk.ru/threads/29118-simulyatsiya-quot-lika-quot-(modifikatsii-spetsialista).html">сделать свою реплику</a>. Об этом кстати <a href="http://www.apofig.com/2018/08/blog-post.html">писал в блоге</a> тоже.<br /></p><p style="text-align: justify;">Только в позапрошлом году мне удалось на форуме таких же инженеров ценителей ретро компов как и я купить один. 7 лет я за ним охотился. И наконец-то он у меня. Я смотрю на него, он смотрит на меня. Паяльник я уже купил. Скоро он откроет мне все свои тайны. Осцилограф только надо освоить. И купить перед тем. <br /></p>
<p style="text-align: justify;">Был период погружения в <a href="http://www.apofig.com/2010/02/blog-post_28.html">уже существуюшие эмуляторы</a> один из которых позволил поиграть в мою любимую игру Клад. <br /></p>
<p style="text-align: justify;">А еще год назад <a href="http://www.nedopc.org/forum/viewtopic.php?f=90&t=9475">я нашел вот тут</a> эмулятор Специалиста (старший брат ЛИКа) написанного на джаве. Он был переделан из эмулятора Синклера, о чем и как Автор интересно пишет на страничках форума. И пару недель рефакторингов (о которых обязательно расскажу позже, т.к. есть и тут чем похвастать) и я пришел к своему очередному pet-проекту <a href="https://github.com/codenjoyme/8080-emulator#readme">эмулятору ЛИКа</a>. </p>
<p style="text-align: justify;">Первый запуск старого кода. Запуск на этом коде ПЗУшек ЛИКа. Исправление команд, т.к. изначально использовался Z80 (синклер) вместо i8080 (ЛИК). Успешный запуск Бейсика. Отладка команд на Экзорцисте. Запуск игры Клад. Оптимизация видео. Адаптация клавиатуры. И много много юнит и интеграционных тестов. <br /></p>
<p style="text-align: justify;"></p><p style="text-align: justify;">А вот всего неделю назад я достал тот самый бит информации из недр моего эмулятора ЛИКа и услышал звучание нескольких моих любимых игр. Я до сих пор помню эти мелодии. Я их столько раз слышал в детстве на магнитофоне. Каждый раз, когда ты хотел поиграть в любимую игру - надо было прослушать ее на магнитофоне. Иногда несколько раз, пока не загрузится без ошибок. Все помню. До мурашек. Послушай как звучит моя любимая игра Клад.</p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/uh5vACfGYOU" title="YouTube video player" width="560"></iframe></center>
<p style="text-align: justify;">В туду еще много всего. Кому это нужно? Да никому. Может быть кто-то еще через 10 лет наткнется на мои наскальные рисунки и порадуется им так же как и я. </p>
<p style="text-align: justify;">Я точно знаю, что сделаю реплику. Я точно знаю, что дизассемблирую и разберусь в том, как устроена игра Клад. И напишу пару своих уровней. Я точно знаю, что разберусь со звуком (над ним сейчас работаю) и он не будет звучать кринжово. И много всего, что еще придет в голову в процессе. Ведь самураю не нужна цель. Только путь.<br /></p><p style="text-align: justify;">Почему? Потому что могу.<br /></p></div></div>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-88229252318904959102023-01-25T10:30:00.007+02:002023-01-25T10:30:58.045+02:00Втоая жизнь Увэ<p>В кого превращается инженер интероверт с потребностью заботиться об окружающих, когда утраичвает самое хорошее, что у него было в жизни.</p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/XYbe4KLetO8" title="YouTube video player" width="560"></iframe></center>
<p>Ух, как эти люди бесят порой, как я его понимаю. И ворота надо обязательно проверить дважды.</p><p></p><p>Отличный фильм. Обязательно буду пересматривать еще раз. Очень рекомендую.</p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-70327580941373430352023-01-24T13:41:00.004+02:002023-01-24T13:41:42.765+02:00Не будите программиста<p>Блядь! Это то слово которое я могу сказать как фидбек, когда меня "рабудили". </p><p>Картинка <a href="https://pikabu.ru/story/ne_otvlekayte_programmistov_5762419">взята тут</a> и наглядно это демонструет. <br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi06XVK7Q5dAek-_DFXdZFBlsyrpbonyflSpFQ_psfiG51w3KMWcjOo4_TuL9QsrKuKKGhosD6GPXal3AoxQcNktb5Z9yg2dIZd-TQOJNVqNAa-lo_AczRZNVw9IkHZ1Io4yClSfVBH2xSrIC4RRtFqCNl4AZwl6k_KCvQsTnGzR7FT_gomvUBFjXQOJA/s1035/1520532613119352287.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1035" data-original-width="700" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi06XVK7Q5dAek-_DFXdZFBlsyrpbonyflSpFQ_psfiG51w3KMWcjOo4_TuL9QsrKuKKGhosD6GPXal3AoxQcNktb5Z9yg2dIZd-TQOJNVqNAa-lo_AczRZNVw9IkHZ1Io4yClSfVBH2xSrIC4RRtFqCNl4AZwl6k_KCvQsTnGzR7FT_gomvUBFjXQOJA/w432-h640/1520532613119352287.jpg" width="432" /></a></div><p style="text-align: justify;">Есть вещи, в которых важен поток. Я работаю над кодом. Я пишу. Я сочиняю. Я медитирую в трансе. Я сплю. Я еду на мотоцикле. Есть дела, в которых нужен весь мой однопроцессорный мозг. </p><p style="text-align: justify;">Статья была хорошая. "<a href="https://pikabu.ru/story/ne_budite_programmista_1920464">Не будите программиста!</a>". Вот сколько лет ей, столько лет перечитываю и аплодирую стоя. <br /></p><p style="text-align: justify;">Я запираюсь в комнате. Одеваю наушники. Включаю рабочу музыку. Ищу концентрации. Ловлю поток. И творю. Только так делается что-то значимое, что потом можно будет вздоить в режиме менеджера. Все эти митинги со слайдами и презентациями, планированием и ОКРами - все это будет потом. Вначале был поток. И был творец. И была мысль. И они снились друг другу. Пока не пришел кто-то и не разбудил... А чтобы этого не случилось - пару моих инструментов для потока. <br /></p><p style="text-align: justify;">Беруши для сна. Уши будут болеть первую неделю, потом привыкнут. Они не дешевые, одноразовые, быстро пачкаются и мыть их нельзя (их такими сделали), но если обернуть в тоненькую стрейч пленку - ее можно сменять хоть каждый день. <br /></p><p style="text-align: justify;">Рабочая музыка для концентрации. Ее слушать только во время работы и больше нигде. Включай эту музыку в момент когда вдруг не стоит на работу, а очень надо - будет тебе эрекция. Еще хочу наушники с активным шумоподавлением. Кто пробовал - делитесь.<br /></p><p style="text-align: justify;">Начать делать что либо первые 5 минут. Потом втянешься и не заметишь как пришел поток. Хочется гулять - надо прыгнуть в обувь и выйти из дому. Любое путешествие начинается с первого шага. Так в любом деле. Заставлять себя, но не более 5 минут. <br /></p><p style="text-align: justify;">Запираться в комнате. Прям барикадироваться. Еще можно повесить на дверь табличку "не стучать, работают люди". Мне так и не получилось объяснить семье почему концентрация - это так важно. Обижаются. Но можно договориться, что тебя не будет 2 часа, а потом ты вернешься и обязательно все починишь/заклеишь/переустановишь.<br /></p><p style="text-align: justify;">Работать ночью. Очень креативно. Но так же вредно для здоровья. Если 10 лет назад я легко мог позволить себе не спать 1 ночь. То сейчас любое лег не до 00:00 - завтра день не такой продуктивный, как мог бы. Но ночью круто. Особенно от 2х до 5 утра. <br /></p><p style="text-align: justify;"></p><p style="text-align: justify;">Больше гулять. На прогулке приходят идеи. Ответы на открытые вопросы, которые были заданы ранее. </p><p style="text-align: justify;">Закрывать глаза на 5 минут и молчать. В любое время, когда чувствуешь что надо еще чуть-чуть поднажать, а голова уже не варит. 5 минут с закрытыми глазами, где бы ты ни был/а. В лифте. В автобусе. На совещании. В туалете. Просто выключи свет. Отключи мозг от 80% информации, которая поступает к нему через глаза. Если при этом еще выключить звук - вообще огонь. Пару минут. Можно с глубокими вдохами - это поможет выключить и эмоцию. </p><p style="text-align: justify;"><a href="https://rikonw.ru/ne-budite-programmista-statya-s-habra/">Еще один хороший пост на эту тему</a> с анализом профдеформаций. Подтвердаю, все так. И не меняется с течением времени. Я менеджером вроде как 6 лет уже работаю. Все такой же задрот. <br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-79423463572880301172023-01-24T12:55:00.002+02:002023-01-24T12:55:14.040+02:00What I've felt what I've known never shined through in what I've shown<p>Всегда любил ее. Сегодня узнал почему. Просто пусть побудет тут.</p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/Ckom3gf57Yw" title="YouTube video player" width="560"></iframe></center>
<p>Источник: <a href="https://www.amalgama-lab.com/songs/m/metallica/the_unforgiven.html">https://www.amalgama-lab.com/songs/m/metallica/the_unforgiven.html</a><br />
© Лингво-лаборатория «Амальгама»: www.amalgama-lab.com/.</p>
<table>
<tbody><tr>
<td>
New blood joins this earth
</td>
<td>
Новая кровь пришла на землю
</td>
</tr>
<tr>
<td>
And quickly he's subdued
</td>
<td>
Он быстро подчинился.
</td>
</tr>
<tr>
<td>
Through constant pain disgrace
</td>
<td>
Через постоянную боль и унижения
</td>
</tr>
<tr>
<td>
The young boy learns their rules
</td>
<td>
Молодой человек учит их правила.
</td>
</tr>
<tr>
<td>
With time the child draws in
</td>
<td>
Со временем ребенок втянулся,
</td>
</tr>
<tr>
<td>
This whipping boy done wrong
</td>
<td>
Этот избитый мальчик сделал ошибку.
</td>
</tr>
<tr>
<td>
Deprived of all his thoughts
</td>
<td>
Лишенный всех своих мыслей,
</td>
</tr>
<tr>
<td>
The young man struggles on and on he's known
</td>
<td>
Молодой человек борется снова и снова, он знает,
</td>
</tr>
<tr>
<td>
Avow unto his own
</td>
<td>
Дал слово сам себе,
</td>
</tr>
<tr>
<td>
That never from this day
</td>
<td>
Что никогда с этого дня
</td>
</tr>
<tr>
<td>
His will they'll take away
</td>
<td>
Они не заберут его волю.
</td>
</tr>
<tr>
<td>
<br /></td>
<td>
<br /></td>
</tr>
<tr>
<td>
[Chorus:]
</td>
<td>
[Припев:]
</td>
</tr>
<tr>
<td>
What I've felt
</td>
<td>
Что я чувствовал,
</td>
</tr>
<tr>
<td>
What I've known
</td>
<td>
Что я знал,
</td>
</tr>
<tr>
<td>
Never shined through in what I've shown
</td>
<td>
Никогда не просвечивало сквозь то, что я показывал.
</td>
</tr>
<tr>
<td>
Never be
</td>
<td>
Не быть,
</td>
</tr>
<tr>
<td>
Never see
</td>
<td>
Не видеть,
</td>
</tr>
<tr>
<td>
Won't see what might have been
</td>
<td>
Не увижу, что могло бы быть.
</td>
</tr>
<tr>
<td>
What I've felt
</td>
<td>
Что я чувствовал,
</td>
</tr>
<tr>
<td>
What I've known
</td>
<td>
Что я знал,
</td>
</tr>
<tr>
<td>
Never shined through in what I've shown
</td>
<td>
Никогда не просвечивало сквозь то, что я показывал.
</td>
</tr>
<tr>
<td>
Never free
</td>
<td>
Не свободный,
</td>
</tr>
<tr>
<td>
Never me
</td>
<td>
Не свой,
</td>
</tr>
<tr>
<td>
So I dub thee unforgiven
</td>
<td>
Так, я нарекаю тебя непрощенным.
</td>
</tr>
<tr>
<td>
<br /></td>
<td>
<br /></td>
</tr>
<tr>
<td>
They dedicate their lives
</td>
<td>
Они посвятили свою жизнь
</td>
</tr>
<tr>
<td>
To running all of his
</td>
<td>
Тотальному контролю над ним.
</td>
</tr>
<tr>
<td>
He tries to please them all
</td>
<td>
Он пытается угодить им всем,
</td>
</tr>
<tr>
<td>
This bitter man he is
</td>
<td>
Этот горький человек — он.
</td>
</tr>
<tr>
<td>
Throughout his life the same
</td>
<td>
Уже очень долго его жизнь не меняется,
</td>
</tr>
<tr>
<td>
He's battled constantly
</td>
<td>
Он сражается не переставая,
</td>
</tr>
<tr>
<td>
This fight he cannot win
</td>
<td>
Он не может победить в этой борьбе.
</td>
</tr>
<tr>
<td>
A tired man they see no longer cares
</td>
<td>
Уставший человек, которого они видят, ко всему безразличен.
</td>
</tr>
<tr>
<td>
The old man then prepares
</td>
<td>
Старик готовится
</td>
</tr>
<tr>
<td>
To die regretfully
</td>
<td>
Умереть сокрушенным.
</td>
</tr>
<tr>
<td>
That old man here is me
</td>
<td>
Этот человек — я.
</td>
</tr>
<tr>
<td>
<br /></td>
<td>
<br /></td>
</tr>
<tr>
<td>
[Chorus: 2x]
</td>
<td>
[Припев: 2x]
</td>
</tr>
<tr>
<td>
What I've felt
</td>
<td>
Что я чувствовал,
</td>
</tr>
<tr>
<td>
What I've known
</td>
<td>
Что я знал,
</td>
</tr>
<tr>
<td>
Never shined through in what I've shown
</td>
<td>
Никогда не просвечивало сквозь то, что я показывал.
</td>
</tr>
<tr>
<td>
Never be
</td>
<td>
Не быть,
</td>
</tr>
<tr>
<td>
Never see
</td>
<td>
Не видеть,
</td>
</tr>
<tr>
<td>
Won't see what might have been
</td>
<td>
Не увижу, что могло бы быть.
</td>
</tr>
<tr>
<td>
What I've felt
</td>
<td>
Что я чувствовал,
</td>
</tr>
<tr>
<td>
What I've known
</td>
<td>
Что я знал,
</td>
</tr>
<tr>
<td>
Never shined through in what I've shown
</td>
<td>
Никогда не просвечивало сквозь то, что я показывал.
</td>
</tr>
<tr>
<td>
Never free
</td>
<td>
Не свободный,
</td>
</tr>
<tr>
<td>
Never me
</td>
<td>
Не свой,
</td>
</tr>
<tr>
<td>
So I dub thee unforgiven
</td>
<td>
Так, я нарекаю тебя непрощенным.
</td>
</tr>
<tr>
<td>
You labeled me
</td>
<td>
Ты назвал меня,
</td>
</tr>
<tr>
<td>
I'll label you
</td>
<td>
Я назову тебя.
</td>
</tr>
<tr>
<td>
So I dub thee unforgiven
</td>
<td>
Так, нарекаю тебя непрощенным.
</td>
</tr>
</tbody></table>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-57255375713667281002023-01-24T10:19:00.000+02:002023-01-24T10:19:00.875+02:00Все яйца в одну кошолку<p>Встретил утро просматривая архивы блога, ютьюб канала, фотки в пикаске (которой уже давно нет), коммиты гитхаба - столько всего было и как-то прошло. Оно вроде как есть, но рациональной пользы (да хотя бы фидбека) мне не приносит сейчас. Ты перестаешь генерировать и перестаешь получать любой фидбек. Странно, но ведь счетчики просмотров продолжают тикать - где ваши фидбеки за эти все годы? А все дело в том, что фидбеком всегда делились на энергию, а она в процессе, а не в результате.</p><p></p><p>
</p><center><img border="0" data-original-height="960" data-original-width="1280" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_5tbK3-xxdqgKhV0EhAfbr-mtX-DzZq0a48QxBMP4Fqv55tDQvP9l0qEcV2sdh_ZsB8drCIqh0pSmyeadlCmUulOCH-3z7sXNIUcgtkgZQMprtVJZcnp0lukaL5H4FPgL0QIM1VQjCUJfz7kofZso7HlkXBS4XLFvhMG1jkYukIkelm8Yyf4BdhybfA/w640-h480/%D0%A4%D0%BE%D1%82%D0%BE.jpg" width="640" /></center><br />
Раньше я делал много всего и сразу. И мне постоянно казалось, что я расфокусирован. Мне так говорили, а не верил им но постоянно сомневался. Некоторым моим менеджерам было невдомек как обуздать это творчество. А потом я сам занялся предпринимательством - пояывился фокус на проекте. Дел триолиион, но проект один. И этот фокус убил вначале 1 проект, а потом и второй. Не проект, а меня в нем. А потом и проект, т.к. проект был моим продолжением. Первый из за усталости и невозможности пробить стеклянный потолок. Второй хоть и не убит но из за кризиса, смены руководства и моей неготовности "показать деньги" его поставили на большую такую паузу. <br /><br />
Чему я научился? Не должно быть одного того самого дела. Не для меня. Ребенок рождаясь в первые годы забирает все соки родителя. Родитель за первый год привыкает и продолжает относиться к уже повзрослевшему ребенку как к грудничку. А нужно сепарироваться. Нужно готовить к будущей жизни. Чтобы потом в какой-то момент отпустить. <br /><br />
Статья Ивана Пирога <a href="https://www.ivanpirog.com/posts/spontannoe-planirovanie-dlya-tex-kto-nenavidit-tajm-menedzhment/">"Спонтанное планирование для тех, кто не навидит менеджмент"</a> и ее вторая часть <a href="https://www.ivanpirog.com/posts/formula-uspexa-spontannoe-planirovanie-i-zhizn-v-potoke/">"Формула успеха: спонтанное планирование и жизнь в потоке"</a>, а так же <a href="https://books.google.com.tr/books/about/%D0%9A%D0%BD%D0%B8%D0%B3%D0%B0_%D0%B4%D0%BE%D1%81%D1%82%D0%B8%D0%B3%D0%B0%D1%82%D0%BE%D1%80%D0%B0.html?id=ImTlDQAAQBAJ&redir_esc=y">"Книга Достигатора"</a> Тимура Гагина и Алексея Кельина - те штуки, которые возвращают мне мою расфокусированность. Ветренность, как когда-то сказал мой научный руководитель. В этом моя сила. Объединять необъединяемое. <p></p><p></p><p>
Сегодня утром у меня это получилось в очередной раз. Я еще раз себе подтвердил, что все еще хочу собирать большие аудитории людей для обмена энергиями с ними. Да, возможно природа этого явления в детских травмах, но не буду обесценивать и скажу иначе - в том пути, который прошла моя нейросетка от рождения пока ее прокачивали событиями люди, окружавшие меня. И теперь сетка готова. 6 лет был с 1 проектом. Был не на людях, а в тени, за кулисами сцены, которую построил с коллегами. Я до сих пор верю в потенциал проекта и у меня есть шанс в ближайшие месяцы продать бизнесу новый концепт. Над этим и работаю. Но я точно знаю, что больше не будет одного единственного проекта. И точно начнет проявляться что-то, что я держу для "как вы видите следующие 5 лет своей жизни?". Держать это я буду в секрете. Пока. Так советуют люди, которым я верю. <br /><br />
Расфокусированность ирационалов с навыком объединения необъединяемого - то что спасет мир. Конечно же вместе с рационалами и их жизненно необходимыми навыками. </p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-4022375823173731342022-01-20T11:31:00.000+02:002022-01-20T11:31:05.210+02:00На ковчеге были динозавры? Сегодня ютьюб показал пару видосов.
<center><iframe width="560" height="315" src="https://www.youtube.com/embed/y8_zVVDvkM0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></center>
Вот интересно, если бы я пришел на зачет к преподавателю и сказал бы, что конъюнкции не существует, я верю то, что ее нет. Так как в священном писании сказано, что бы мне он ответил? Вот тут например
<center><iframe width="560" height="315" src="https://www.youtube.com/embed/Qrn_cQCFlL0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></center>
Ты либо за научный подход и критичский способ мышления либо за веру в священное писание и авторитет тех, кто дольше чем ты его толкует. Какие общие подходы вырабатывать, если фундаментальные аксиомы противоречат друг другу.
<center><iframe width="560" height="315" src="https://www.youtube.com/embed/QId7MiwSoPI?start=504" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></center>
А еще стоит вспомнить, что религия делала с наукой, когда у нее было больше власти.
<center><iframe width="560" height="315" src="https://www.youtube.com/embed/4OqD3tUYOA8?start=504" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></center>
Это печально лишь только.А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-58322611370653330672022-01-17T16:02:00.000+02:002022-01-17T16:02:15.544+02:00Почему инженер делает что-то? Потому что может!<p>В одном из чатов меня спросили, а почему мы делаем CodingDojo ивенты для инженеров. </p><p><span style="background-color: #cccccc;"></span></p><blockquote><span style="background-color: #cccccc;">Почему, по вашему мнению, взрослым людям интересно заниматься такой достаточно детской забавой? (лично авторам в том числе 🙂 ) Что мотивирует?</span></blockquote>Инженер это такой подвид Homo Sapiens, который делает что-то казалось бы совсем не нужное потому что может.<br /><br /><center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/5M5ao1D4jWo" title="YouTube video player" width="560"></iframe></center><br />Я вот вчера пол ночи ковырял эмулятор старого бытового компьютера ЛИК, который был придуман в 1988 году. Просто потому что могу. Это не нужно для прогресса человечества сегодня. Но моими наработками будут пользовался некоторые инженеры, которые так же как я ценят ретро компьютеры. А возможно кого-то из них мои наработки вдохновят на какие-то другие свершения. Или их свершения вдохновят других инженеров, которые вдохновят других инженеров, которые... Быть может какие-то из свершений будут уже в области, которая поможет человечеству сегодня. <br /><br />Мы в контексте CodingDojo делаем то, что заряжает инженеров, а потом они возвращаются на свои проекты и творят там чудеса. <br /><br /><center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/b0tUkmv_D8I" title="YouTube video player" width="560"></iframe></center><br />или что-то вообще на другой планете<br /><br /><center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/kiRHaFAKAHU" title="YouTube video player" width="560"></iframe></center><p><br />По сути это все инженерные задачи, огромные (типа полета на Марс робота made in Earth) - складываются из больших, большие из множества маленьких, маленькие решаются за пару вечеров. Какая разница на чем качать свой инженерный ум? Он прокачивается серии "это не возможно", которые он сделал возможным этой ночью. А начал это путешествие без уверенности в успех, но со знанием что путешествие будет увлекательным. <br /><br />Делая музыку на старых дисководах, откапывая проект 25 летней давности или играя в Coding Challenge<br /><br />Ум инженера требует головоломок. Без них он скучает. Скучающий инженер - опасность для проекта, т.к. он все равно найдет себе развлечение. И хорошо, если на стороне. А инженер наполненный энтузиазмом удивляет. Бизнесу это "тупо" выгодно - создавать оазисы для своих инженеров, зоны рекреации. Места, где нет давления менеджмента, где нет требований, где можно сделать так, как хочется, где есть только задача и острый инженерный ум, купа таких же ботанов как ты, двигающих мир вперед. </p><p>Думаешь это все "войти в IT" и большие зарплаты двигает нас в будущее? Нет. Это делают такие люди, как инженер в видео ниже<br /></p><p></p><p></p><center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/0TTXVMMWlbU" title="YouTube video player" width="560"></iframe></center><p></p><p></p><p>Почему он cделал то, что сделал? Потому что мог.<br /></p><p></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-42217451878783648622022-01-17T15:26:00.003+02:002022-01-17T16:20:04.976+02:00Как улучшить себе жизнь со скриптами bash/btach<p>Сегодня я хотел написать этот пост, но краткое вступление раздулось до <a href="http://www.apofig.com/2022/01/100.html">огроменного ставшего в результате самодостаточным поста</a>. Тут же я поделюсь серией подобных практик в одной узкой области - сборка и поставка приложения. А пост этот был написан с использование другой практики - make manual from chat log. Когда ты с коллегой в чате уже обсудили какой-то вопрос, ты настрочил много текста и теперь хочешь его как-то повторно использовать. Так что этот пост, как и многие другие - во многом вдохновлен обсуждением вопроса в чате, скопипащенный оттуда и немного отредактированный. </p><p>Итак погнали. Я ленив, а потому если команду в консоли bash/batch я ранаю второй раз за сегодня, тут же останавливаюсь и начинаю писать bash/batch скрипт: bash для linux, batch для windows. За последние пару лет эта практика меня сильно прокачала в этих сприктовых языках программирования. Если раньше мне приходилось гуглить как делается в OS так или иная операция или выполняется та или иная команда, то сейчас я гуглю особенности различных языковых конструкций: if/else/while/function/ect, как делаются те или иные expression и так далее. Этот язык программирования (вообще правильно <a href="https://en.wikipedia.org/wiki/Command_language">command language</a>) весьма специфический и более того их два диалекта - для windows и для linux. Почему диалекта, потому что очень многое слизано (как мне кажется) у windows с linux. Отличия все же приходится гуглить. </p><p></p><p>Раньше я писал скрипты для обоих OS. Но в последнее время склоняюсь в сторону linux, а для тех, кто работает под windows предлагаю для запуска моих bash скориптов использовать что-то типа <a href="https://www.cygwin.com/">cygwin</a> или <a href="https://www.mingw-w64.org/">mingw</a>. Очень в немногих местах приходится проверять OS и менять поведение скрипта в зависимости от OS. Но это куда лучше чем писать две версии скриптов, либо лишать windows пользователя инструмента автоматизации. </p><p>Идем дальше. Загнать тот скрипт, который ты обычно ранаешь в консоли в текстовый файл и назвать с расширение bat/sh это первый шаг, но этого недостаточно. Часто новички в DevOps часто используют команду cd для того, чтобы направиться в определенную папку, и там уже выполняют команду. Я же пришел к тому, что каждую команду стоит выполнять в абсолюте.<br />Все что выполняется относительно, всегда потом дает повод поиграться с ним в самый неподходящий момент. <br /></p><p><span style="font-family: courier;"><span style="background-color: #cccccc;"></span></span></p><blockquote><span style="background-color: #eeeeee;">cd ./some/place/in/project<br />ls -la<br />cd ../../..<br />cat file.txt</span></blockquote> <p></p><p></p><p>В суппорте такой подход требует от меня всегда помнить "где я?". То есть для каждой строчки надо помнить 2 вещи - ЧТО делаем, ГДЕ делаем. А если ты юзаешь всегда только абсолютный путь, тогда все чисто. <br /><span style="background-color: #cccccc;"><span style="font-family: courier;"></span></span></p><blockquote><span style="background-color: #eeeeee;">ls -la /srv/app/some/place/in/project<br />cat /srv/app/some/file.txt </span></blockquote><p></p><p>Более того, рекомендовано использовать такие команды даже когда ты работаешь в консоли руками. Если говорить про linux то там в папке твоего юзера есть ~/bash_history файл и в нем все команды когда-либо выполняемые тобой на этой машине. Очень информативно видеть там самодостаточные команды, выполнив которые из любого места системы будет один и тот же эффект.</p><p>Но как быть с дублированием? Допустим я не хочу постоянно вбивать /srv/app/some </p><p><span style="font-family: courier;"></span></p><blockquote><span style="font-family: courier;"><span style="background-color: #eeeeee;">ls -la /srv/app/some/place/in/project<br />cat /srv/app/some/file.txt</span></span></blockquote><p></p><p>В скрипте (bash) сделать так</p><p><span style="font-family: courier;"></span></p><blockquote><span style="font-family: courier;"><span style="background-color: #eeeeee;">root=$(pwd)<br />ls -la $root/place/in/project<br />cat $root/file.txt</span></span></blockquote><p></p><p>В batch вот так </p><p><span style="font-family: courier;"></span></p><blockquote><span style="font-family: courier;"><span style="background-color: #eeeeee;">set root=%cd%<br />dir %root%\place\in\project<br />type %root%\file.txt</span></span></blockquote><p></p><p>Следующим шагом я бы улучшил информирование в консоли (bash). </p><p><span style="font-family: courier;"></span></p><blockquote><p><span style="background-color: #eeeeee;"><span style="font-family: courier;">BLUE=94<br />GRAY=89<br />YELLOW=93<br /><br />color() {<br /> message=$1<br /> [[ "$2" == "" ]] && color=$YELLOW || color=$2<br /> echo " [${color}m${message} [0m"<br />}<br /><br />eval_echo() {<br /> command=$1<br /> [[ "$2" == "" ]] && color=$BLUE || color=$2<br /> color "${command}" $color<br /> echo<br /> eval $command<br />}</span></span></p><p><span style="background-color: #eeeeee;"><span style="font-family: courier;">eval_echo "set root=%cd%"<br />eval_echo "dir %root%\place\in\project"<br />eval_echo "type %root%\file.txt"</span></span></p><p><span style="background-color: #eeeeee;"><span style="font-family: courier;">echo<br />color "Press Enter to continue"<br />read </span></span></p></blockquote><p>И тогда вывод будет намного более приятным. Перед выполнением лога работы каждой команды будет напечатана другим цветом сама команда. А в самом конце скрипта подождем нажатие Enter с тем, чтобы убедиться что оператор все осознал и возражений не имеет. </p><p>
<img border="0" data-original-height="791" data-original-width="472" src="https://blogger.googleusercontent.com/img/a/AVvXsEj0yHVxIA5M7fIL9U5_iSKBNmRG9j60lL2WXDpR4GlepgyoHX7AsCLbAX_1b45FalqVYoLbc_NN-ZE6dBqCCB8OYE1GkCtvxJ2FOZzkafzWToPduwo2nwnV1XXkjSmOS2bijn25IMWUk6dBQ5IaCjSaDB9cbCBJsSX9cmAd1UyRhbCsmSJsnUaZ0Y00Pg=s16000" /> </p><p>Согласись так нагляднее, чем видеть все то же самое только без цветных строчек. </p><p>Удобство этого подхода в том, что вместо переменных типа $root будет вставлено реальное значение и на экране напечатается информативная команда, именно та, что будет реально вызвана. </p><p>А если не хочется копипастить из скрипта много кода, то напомню, что самый минималистичный вариант eval_echo это <span style="font-family: courier;"></span></p><p><span style="font-family: courier;"></span></p><blockquote><span style="background-color: #eeeeee;"><span style="font-family: courier;">eval_echo() {<br /></span><span style="font-family: courier;"><span style="font-family: courier;"> echo " [94m$1 [0m"</span><br /> eval $1<br />}</span></span></blockquote><span style="font-family: courier;"></span> <p></p><p>Обрати внимание, что в этом коде есть ESC символ. </p><blockquote><p> <img alt="" src="data:image/png;base64,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" /></p></blockquote><p>Благодаря этому символу в linux консоли можно рисовать цветом. Под bacth есть подобный подход.</p><blockquote><p><img alt="" src="data:image/png;base64,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" /></p></blockquote><p>Все это сильно позволяет ускориться во время выполнения обычных рутинных действий: залить ветку на github со всеми subrepo, pull всех subrepo, автоматическая генерация каких-то ресурсов, создание snapshot для всех артефактов, создание запускного jar для каждого компонента с автоматически сгенерированными скриптами запуска, запуск сервера в целях отладки в определенной конфигурации, дамп базы и так далее. Все это делалось вручную раньше, а сейчас на это не тратится время. Время (сильно меньше) тратится на поддержание скриптов. Это экономически выгодно, иначе небыло бы DevOps с CI/CD и мы собирали war вручную или с Ant, заливали бы его в папку Tomcat на сервере, разбирались бы с конфликтом jar, как делали это 10 лет назад. Деплой бы занимал не 1 клик и 10 минут, а 1 час и много ручной работы. <br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-62519209336640575342022-01-17T14:17:00.004+02:002022-01-17T14:17:36.396+02:00Инженерные практики могут ускорить тебя в 100х раз<p>Я человек ленивый, а потому если мне приходится ранать команду в консоли второй раз - сажусь за написание скрипта. И первых пару запусков все внутри меня радуется, поскольку автоматизировав рутину я ускорился на ровном месте. Теперь моя производительность немного выше того, кто не пользуется этим подходом. </p>Почему я коллекционирую различные ускорялки? Математика проста. Пусть это конкретное решение мне даст +15% в производительности. Какое-то другое решение даст +15%. Еще где-то что-то прочитаю и попробую, снова +15%. Складываем вместе 10 подобных решений и получаем 1.15 ^ 10 = 4.04. То есть 10 подходов ускоряющих тебя всего лишь на 15% в сумме дают + 404%, а это 4х в скорости! Подходов за 10 летний опыт может собраться на порядок больше. И для 50 штук прирост будет уже 1083%, 10x. А и это 15% роста продуктивности это так, самый минимум из того, что новая практика тебе может дать. Часто введение нового подхода сама по себе в разы тебя ускоряет. <p>Внимательний читатель заметит, что любой новый подход в разработке облагает автора налогом. Естественно надо закладывать время на суппорт. Часто разработчик принимает решение сам, буду ли заморачиваться с новым кодом и его суппортом или по старинке сделаю, и делает это на основе критерия. Если я скажу, что новый подход даст тебе прирост в производительности в 10 раз, а при этом потребует от тебя всего лишь по 5 минут в день больше времени - дурак не согласится. Но обычно все не так. Времени на реализацию решения надо потратить сегодня 2-3 часа, а потом еще на суппорт решения раз в неделю по 0.5 часа. А прирост будет 10-20%, что уже не так леко прочувствовать, как 2,5 часа. На одной чаше весов - вклад в часах единоразовый с небольшим вниманием, а на другой % прироста производительности. % складываются иначе, чем часы. Копить % выгоднее, чем экономить время. Не всегда, но часто.<br /></p><p></p><p>Правда, далеко не все дивиденды от инженерных полдходов можно "складывать" как описано выше. Есть независящие инструменты. Ну например я кодирую в Idea (она удобнее), а на сервере у меня все в docker (легче чем инсталить все на host машину). Там на X% ускорился, тут на Y%. Но так как работаю я ИЛИ в идее ИЛИ с докером на сервере, то % берется не от 168 рабочих часах в месяце, а от всего времени в IDE и отдельно всего времени во время обслуживания сервера. Результат складываем и "X% + Y%" дает ((1+X/100)*develop_time + (1+Y/100)*deploy_time). И в этом случае время проинвестированное в разработку и поддержание инструмента стоит оценивать критичнее. Но есть и фундаментальные подходы, скажем как следование принципам BabyStepsRefactoring, CleanCode, UnitTesting, TestFirst. Выгода от использования этих принципов ощущается одновременно и влияет друг на друга, потому формула суммирования будет более вкусной. Возможно не (1+X/100)*(1+Y/100) - это крайность. Но чем более влияния подходов друг на друга, чем чаще они используются, тем ближе мы к пермножению процентов, а не суммированию. <br /></p><p>Надеюсь никтон не будет спорить в наше время с тем фактом, что рефакторить код необходимо, если ты хочешь хоть как-то влиять на энтропию системы. Держать код в чистоте - значит помогать себе и другим читателям понимать, что тут происходит. Делать рефакторинг можно грубо зарывшись по уши в код и потом тратя время на исправление всех ошибок компиляции, отловку багов, а можно элегантно - вооружившись компилятором ide и юнит тестами быстро привести код в рабочий вид. Но параллельно с этим всем можно производить рефакторинг маленькими шажками, что позволит исключить дебаг из процесса. А если нужна новая функциональность, то использования подхода TestFirst и сключит дебаг так же из процесса разработки. Привет TDD! Каждая из практик ценна сама по себе, но вмсте они дают возможность избавиться от Debug вообще. </p><p>Debug - самая сложная и неуправляемая, а потому и дорогостоящая часть в разработке. Во время Debug ты мало что понимаешь - только тыцаешь 2-3 клавиши и смотришь в стек в ожидании инсайта. А без юнит тестов рефакторинг опасен, но если ты и осмелишься - много багов уйдет на прод. А без рефакторинга сложно и за CleanCode следить. Так вскоре техдолг не даст менять сисему как того нужно бизнесу. В какой-то момент исправление 1 баги будет порождать 2 новых. Тогда лучшее, что можно сделать - заморозить его. Ну или покрыть модуля автогенерируемыми юнит тестами, затем взяться за рефакторинг модулей с целью навести порядок в модели и сделать код чуть более clean. Затем выкинуть старые автогенерируемые тесты и написать нормальные, читабельные. Этот долг придется выплатить, если хочется продлить жизнь проекту. И лучше тут пользовать подходом ПрицнипСкаута. Сделать место стоянки после себя чище, чем оно было до тебя. Каждый день плати чуть-чуть времени уделяя этим инструментам: CleanCode + Refactoring + UnitTesting. И проект проживет дольше. </p><p>Это один из примеров связки практик. Есть и другие практики. И тот из нас, кто научится использовать их по максимуму будет производительнее чем тот, кто не будет в 100 раз, в 1000 раз, а может и в 10000 раз. Возьмем студента новичка без опыта разработки - сложная задача может просто его застопорить на недели (если вообще задача будет решена), тогда как опытный миддл сделает ее за два дня. Вот и решай на сколько более производительный тот, кто сделал работу в сравнении с тем, кто не сделал ее. Я же верю в то, что хоть в среднем по индустрии Middle не сильно отличается от Senior (так просто устоялось), их производительность может отличаться в весятки, а то и сотню раз. Вопрос в том, остановился ли Senior в развитии на уровне инструментария Middle и дальше качает только SoftSkills или продолжает поиск инструментов его ускоряющих. <br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-5832496299323971042022-01-12T13:07:00.004+02:002022-01-12T13:07:44.199+02:00Нейросети уже кодят вместе с нами<p>Ждал эту фичу от StackOverflow но ее предложил Github. Программирование с этой технологией становится еще более высокоуровневым. </p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/e9Y9NKWCEPk" title="YouTube video player" width="560"></iframe></center><p>Какие трудности. Ну кроме того, что еще больше информации будет собирать о нас большая компания добра (этим в наше время уже никого не напугать) больше волнует общественность нарушение авторских прав. Во-первых копайлот учится на открытом исходном коде, а некоторые лицензии могут запрещать использование этого кода в проприетарном приложении (привет GNU GPL). Во-вторых код из окружения разработчика утекает на сервера Microsoft и вероятно на них тоже обучается нейросеть, что может нарушать авторские права проекта. Но прогресс не остановить, я уверен что у ребят найдутся ресурсы, чтобы порешать все и технология увидит своего покупателя. Кстати да, я более чем уверен что подписка будет платной. </p><p>Слышу ворнинги разработчиков порталов с коллекцией алгоритмов <a href="https://en.wikipedia.org/wiki/HackerRank#See_also">типа HackerRank</a>, ведь теперь любому участнику в разы проще вырваться из низов используя только Copy-Past Driven Development. Но я уверен, что и они решат этот вопрос. <br /></p><p>Что классно - что порог вхождения заметно уменьшился. Теперь не надо будет учиться гуглить на StackOverflow новичку в программировании (с этим замечены были трудности), но все так же надо будет учиться декомпозировать, отлаживать, рефакторить, ООП и т.д. </p><p>Мне видится потенциал этой технологии в области TDD и Refactoring. Уже сейчас такие чудные инструменты как Intellij Idea очень классно подсказвают в рефакторинге что можно упростить. И те из нас, кто очень крут в рефакторинге просто потому что он его изучал глубоко постепенно сдает свои позиции тем, кто только вошел в индустрию. Два клика мышкой и смотришь - было стало. А теперь представь, что сможет Copilot глядя на все Diiff во всех репозиториях - там закодированы бесчисленные рефакторинги. </p><p>Другой килер фичей может стать подключение TDD методологии разработки в Copilot. То есть если сделать возможным задавать запрос на написание метода через тесты, тогда и отладку можно отдать нейросети. Пусть перебирает код, пока не пройдут тесты. Вот это будет здорово! Это еще сильнее снизит порог вхождения и уберет из процесса разработки Debug - самую сложную часть, которая пока еще остается нашим козырем. </p><p>Будущее уже наступило. Пристенитесь, мы продолжаем ускоряться.<br /></p><p><br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-71179907458734392602022-01-11T15:44:00.012+02:002022-01-11T16:45:32.409+02:00Мое отношение к поиску кандидата его интервью и найму<div style="text-align: justify;"></div><p style="text-align: left;">В одноим из чатов коллега спросил про мое мнение про найм на работу. Поделюсь тут. <span style="background-color: black;"><span style="color: white;">Скажу сразу, что это мое лично мнение и никак не отражает стратегии или процессы компаний, в которых я трудился, тружусь сейчас или буду трудиться в будущем. Это мой лично собирательный образ процесса найма. По нему я пришел к такому вот дзену. В процессе буду переключаться с "я инженер" на "я менеджер" так как периодически попеременно бывал в этих ролях: и собеседовал, и собеседовался. </span></span><br /><br />Собеседования не эффективны по своей природе. Я как интервьюер в первые пару минут разговора уже знаю (чувствую) беру ли этого человека к себе на проекте или нет. Все остальное время - "тыкание палочкой" и поиск оправданий для своего изначального чувства. Даже если все идет по процессу - быть субъективным 1 человеку разумному сложно. Первое впечатление задает весь тон собеседования. А еще многое зависит от совместимости психотипов, что ты ел сегодня утром, болит ли у тебя голова, выспался ли, ругался ли ты сегодня утром с представителем ЖЕКа по поводу протекающей канализации под домом, получил ли ты ожидаемый бонус на прошлой неделе и массы других факторов, которыми наполнен каждый день каждого из нас. Разговор будет не про твой опыт - так точно. Скорее про обмен веществ. Но всем будет казаться, что про опыт.<br /></p><p style="text-align: left;">И так, чтобы сэкономить время себе, команде и кандидату - можно посмотреть запись (если таковая имеется) прошлых собеседований. Лучше, если кандидат сам подготовит такое видео, на котором расскажет о всем, о чем гордится в своей карьере. И вместе с резюме (а как сделать "вкусным" и при не очень глубоком опыте - google в помощь) затем отправить в компанию. Там будет ответ. Да или нет. </p><p style="text-align: left;">Если да - отлично! Давайте пообщаемся по существу - как мы (и наш опыт) можем быть полезны друг другу. Если нет - то нет. Все равно, даже если мы проведем классическое интервью - фидбек будет приблизительно такой "спасибо, мы выбрали другого кандидата", "спасибо, мы вам еще напишем (ложь)", "спасибо, вы нам не подходите". Любые попытки разузнать что не так - очень модерируются и в результате ты не получаешь подробного (хоть и неприятного, но потому и полезного) фидбека. Банальные отписки. Или тишина. Да я понимаю почему - тебе как менеджеру проект надо застафить. Вакансий 5. Кандидатов 25. Попробуй всем отписывать подробную обратную связь. А если ты рекрутер - твоя зона внимания уже не 25, а 250 кандидатов. К тому же кандидат может и не услышать (понять) фидбек, обидеться, отпишется и через год, когда станет более опытным не захочет иметь дело с компанией. Сложно тут. Я все же стараюсь давать емкие фидбеки даже когда освобождаю (увольняю) ребят из команды. Но всегда это трогает такие слои, которые не очень приятно поднимать и осознавать коллеге. Но так у него есть шанс. Он получил обратную связь. Ее, эту связь, кстати, хорошо бы получить и для проекта от него. Такую же не удобную. А потом превратить в план работы над ошибками. </p><p style="text-align: left;">Про резюме я сказал. Оно вообще ни о чем не говорит. Написать его любым мне как кандидату ничего не мешает. Масса сататей и видосов на эту тему написаны. Целые разделы IT тренингов этому посвящены. Как высосать коммерческий опыт из пальца. Другое дело отзывы коллег, с которыми трудился. Потому я лично их всегда для себя запрашиваю и сам делюсь. Если не спрашивать - никто и не оставит. Все спешат. Надо спросить и напомнить. И еще раз напомнить. Вот <a href="https://www.linkedin.com/in/alexanderbaglay/">мой профиль</a>, я им горжусь. Я уже не может быть всего над чем трудился с коллегами, но в любой момент смогу прочитать их отзывы. Надеюсь мои отзывы им так же греют. И я никогда не писал того, во что не верил сам, иначе теряется весь смысл. <br /></p><p style="text-align: left;"><img alt="" src="data:image/png;base64,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" /></p><p style="text-align: left;">Но вернемся к резюме. Само по себе резюме в наше время - с точки зрения интервьюера почти ноль. Потому на
собеседовании скорее всего разговор будет не на равных, а с подозрением -
мол ты обманщик и мы сейчас тебя будем уличать. Вот зачем так
неэффективно расходовать свою жизнь? Сколько таких интервью было у меня. Акт самоутверждения через унижения кандидата. Причем попробуй как-нибудь сам взять на собеседовании подобный тон, перехватить инициативу (у вас же СО-беседование) и спросить что-то впроде. А вы сами-то используете на проекте то о чем меня спрашиваете или я буду писать бекенд ендпоинты методом copy past в системе которую нельзя рефакторить потому как тестов вообще нет, а тесты писать нельзя потому, что заказчик не хочет за это платить? Так работы не получить. А если она сейчас не очень то и нужна, то зачем тогда ходить на собеседования? Редкость, когда это делается для поддержания тонуса, но и не честно я считаю по отношению к тем, кому ты не сказал заранее что новой работы не ищешь, а на собеседование идешь из любопытства. Так что чаще всего на собеседовании именно кандидат сидит в неудобной позе. Палочкой в него тычут.<br /></p><p style="text-align: left;">На самом собеседовании это выливается в вопросы типа "а чем отличается интерфейс от абстрактного класса". Не ну вы серьезно? Это ж как надо не доверять резюме кандидата, чтобы начинать с подобного вопроса. Ну или "а давайте по-программируем на листочке A4 и посмотрим как вы соображаете". Очень полезный навык, раскрывающий всю суть инженера. Программирование на листочке. Лет 30-40 назад так и было, но не сегодня.<br /></p><p style="text-align: left;">Я помню свое детство хорошо. Семья не располагала необходимым ресурсами. У меня компьютера не было вообще. Напрашивался программировать по одноклассникам. Им повезло больше. Так вот, когда родители этих одноклассников как бы намекали, что мне пора и я вообще зачастил, а потом одноклассники осторожно морозились, но так, чтобы все было понятно, вот тогда я придумал для себя программировние на листочке. Писал и отлаживал все программы на бумаге, а как только появлялась возможность получить процессорное время - программы я вбивал в комп, компилировал и с фидбеком уходил домой дебажить дальше на бумаге. И о чудо, наловчился писать программы без ошибок. Но кто про это спросит на собеседовании? <br /></p><p style="text-align: left;">Когда я работал в офисе то порой наблюдал за тем, как все окружающие вставали и синхронно шли к кофепоинту. Интернет отключился. Google driven development больше не работает. Работа стала. И с таким настроем ребята принимают решение про трудоустройство на основе написанного кандидатом в блокноте. А ты попробуй выключи интернет и сам попрограммируй хоть два часа к ряду. А ведь в опыте кандидата могли быть и такие показательные моменты, когда в целях пожаротушения без IDE по SSH на удаленном linux prod сервере в консоли в jar (что внутри war, что внутри докера) подменить 1 перекомпилированный вручную класс чтобы NPE исправить не пересобирая всего приложения (допустим, это не возможно). Его ж не спросят. А про давайте двусвязный список напишем в блокноте... Ну такое. </p><p style="text-align: left;">Хочешь посмотреть как кодит кандидат? Запроси его гитхаб. Коллега должен уже что-то сделать до того как предложить свою кандидатуру. Его github аккаунт должен быть живой и показывать что помимо работы и зарабатывания о постоянно в тонусе инженерии. Что-то делает. Его это интересует, он живой. И в коде видно насколько. Как он относится к тестированию. Как он работает с техдолгом. Как у него с документацией и CI/CD. Как у него с алгоритмами. А ты и есть этот инженер и у тебя нет до сих пор гитхаба - опубликуй в нем все свои домашние проектики (я не верю что у тебя их нет). As is. И потом веди их уже так, как если бы за тобой подглядывает весь мир. Мне повезло, у меня проект opensource. Роль на проекте у меня больше менеджерская, но свой вклад в опенсорс я успел сделать. <br /></p><p style="text-align: left;"><img alt="" src="data:image/png;base64,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" /> </p><p style="text-align: left;">Имея базу гитхаб + резюме + продающее видео ты как инженер становишься привлекательнее. Я как менеджер быстро могу понять хочу ли продолжить общение или нет? Может просто опыт не подходит нашему проекту - это можно понять уже через 5 минут. Или по темпераменту человек не впишешься в коллектив - так же 5 минут. Зачем суммарно тратить N часов времени, ведь 2 часа только самого интервью суммарно всех участников, а сколько для подготовки к нему, сколько ментальных сил кандидата. Если мы ускоряемся, а мы ускоряемся, то я хочу иметь возможность общаться за тот же 1 час с 10ю кандидатами. Не меняя в корне подхода интервью, это не возможно. </p><p style="text-align: left;">Идем дальше. Что бы не происходило на интервью, как бы вы друг другу не понравились - все тайное станет явным в процессе триал периода. Инженер пришел помогать бизнесу или зарабатывать не сильно напрягаясь. А раз так, почему бы не проверить это заранее? У меня проект опенсорсный, я могу привлекать контрибьюторов. А тем из них, кто показал лучшие результаты - предлагать место в команде. Мне бы хотелось, чтобы в любом проекте, каким бы он ни был закрытым - нашлась часть работы, ТЗ которой можно было бы опубликовать в интернете без нарушения контрактов. Результат полезен всем. Кандидат имеет шанс показать себя, прокачать свой гитхаб, а может даже и получить плюшку от компании (как фрилансер). Команда получает дополнительную возможность решать свои вопросы, заметить на рынке талантливых ребят, сделать им предложение. Но это ж надо заморочиться. Да и проект у нас закрытый. Собеседования ж привычнее. </p><p style="text-align: left;">Это все про мир, каким бы он мог быть, если бы я переизобретал его с нуля. </p><p style="text-align: left;">К слову про переизобретения. Раньше (на заре моей профессиональной карьеры) все менеджеры твердили, что удаленная работа не возможна. Что мы скажем другим, если тебе разрешим? Как мы проверим вы там шаритесь, а работаете? Как организовать рабочее место? Как быть с безопасностью? И тому подобные вопросы-отговорки без желания (скорее потребности) искать на них ответы. Но меня волновало другое - 2,5 часа на дорогу с/на работу в сутки дают 3 полных рабочих месяца по 8 часов в году или 22 сутки из 365 доступных в дороге. Не с семьей, не за проектом, не за книгой, а в дороге, нюхая выхлоп или потную подмышку соседа в метро. Но я добился удаленной работы за 6 лет до карантинных ограничений и доказал, что офис для работы лично мне не нужен, более того он мешает. А работая из дому проблемы совсем другие возникают, не те, о которых менеджеры переживали - как делать перерывы и не перерабатывать. Сейчас моя команда работает по всему миру. И нам всегда было достаточно для решения любых вопросов чата и созвонов. Это про IT. А офис с печеньками и тенисным столом... Хорошо, что это отходит. Отойдут и собеседования. </p>
<center><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/ZkwqZfvbdFw" title="YouTube video player" width="560"></iframe></center>
<p style="text-align: left;"></p><p style="text-align: left;">Еще момент, называется синдром самозванца. Если кандидат им страдает, у него шансов в моем проекте в разы больше, даже если опыта у него недостаточно. По моему важно не то, сколько опыта ты перелопатил в прошлом, а как ты готов ускоряться в будущем. Если в карьере кандидата все уже было и цель заработать на ипотеку не сильно напрягаясь - продуктивности не будет. Еще год назад студент, который только только стал мидлом будет больше драйва и энергии проекту давать, чем ленивый гуру. При чем тут синдром самозванца? А все просто - в тот момент, когда кто-то решает, что более чем заслужил быть сдесь - начинается его незаметная деградация. Зачем напрягаться, если все и так ок. Зачем делать больше, если это ни на что не повлияет. А я скажу. Есть инженеры которые делают свою работу в 100 раз быстрее других. При чем правда, что зарплаты у них могут и не отличаться заметно. Но не в этом дело. Дело в том, чтобы успеть сделать больше. Успеть оставить след. И шансов больше у того, кто не уверен в своих силах. И все потому, что ему некомфортно если ничего не происходит, он ищет пути. И находит. А есть опытный уверенный в себе, но как окажется поже ленивый синьйор-помидор, он уже все умеет и знает. Я возьму молодного и неуверенного. </p><p style="text-align: left;">Камушек в сторону устоявшегося процесса рекрутинга. Создавая тренинг школу с коллегами мы сталкивались с проблемой наших выпускников - рекрутеру не интересны студенты выпускники. Опытных надо. И отношение к ним не очень. Получается студенты бегают за рекрутерами до получения первого комерческого опыта. А после первого проекта картина разворачивается на 180 градусов - за инженерами бегают уже рекрутеры. Вопрос к рекрутерам - как будут относиться к вам инженеры которым пол года назад на любой их запрос вы отвечали отказом или игнором? И как мне быть с вашими 100500 запросами по всем каналам связи о том, что мне бесконечно надо рассмотреть вашу компанию для нового места работы. Причем все из них мимо. А если вдруг интервью я не пройду по причине какого-то несварения с интервьюером стоит ли включать игнор? Эта игра не в долгосрочную. Ясное дело, что завтра придут другие. Но те, кому вы сказали нет вчера - завтра больше не придут. Я точно не приду.<br /></p><p style="text-align: left;">А к кому придут? К тому, кто интересовался их настроением, их карьерой их жизнью и профессиональной деятельностью тогда, когда не надо было стафить чей-то проект. Где-то так, наблюдая за работами тех немногих рекрутеров, у меня родилась идея назвать их роль не рекрутер для компании, а продюсер для инженера. Как с рок звездами. Пока инженер делает магию на сцене - мы качаем его, помогаем вырасти по карьере, помогаем найти работу или поддерживаем нетворкингом и связями. Инженеру, который сейчас не у дел (выгорел, устарели технологии, болеет, проблемы) очень важна поддержка и кастомные условия, даже если это не выгодно рекрутеру сейчас. Тогда как потом, когда он приведет все дела в порядок - его можно будет выгодно устроить. </p><p style="text-align: left;">Было бы здорово, чтобы между инженером и продюсером были и другие взаиморассчеты, а не прсото бонус рекрутеру за трудоустройство от компании. Но тут уже камень в огород инженера пост советского пространства - он не готов за платить за развитие своей карьеры. Жаль. Это считаю большим упущением. Не все деньги, что инженер заработал - по настоящему его. Часть денег - это инвестиции от работодателя в его будущее. Если деньги были спущены инженером не на самообразование, не на расширение компетенций, а исключительно на жизнь-потребление, в будущем в кризис инженеру будет не просто. А кризис придет.<br /></p><p style="text-align: left;">А пока все ж работает. А значит не трогай! Так жеж?<br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com4tag:blogger.com,1999:blog-7317504408463627049.post-65243547691263903682021-11-04T19:57:00.001+02:002021-11-04T19:57:06.782+02:00Что такое ответственность<p>Вчера узнал истинное значение слова "ответственность" или "responsibility". Это не про "обязан", "надо", "долг", "правила", "вину", "напряг", "гемор", "законы" и "наказание" за невыполнение - это унаследованный совковый менталитет. "Ответственность" - от слова "ответ", а "responsibility" от "response". Иными словами - готовность держать ответ перед реальностью здесь и сейчас. Не завтра, не вчера, а сейчас. "Готовность" скорее не про "нужду", а про внутренний "выбор". Если ты выбираешь держать ответ, что бы ни случилось - значит ты ответственный. Если не желаешь общаться с реальностью в каком-либо вопросе - ты безответственный.<br /></p><p>Как-то так.</p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com1tag:blogger.com,1999:blog-7317504408463627049.post-8018168268847078422021-06-08T08:51:00.009+03:002021-06-08T09:22:06.779+03:00Что для меня ENGX?Мне есть с кем сравнивать. Бывает подключишь человека к проекту - резюме хорошее, собеседование прошло хорошо, вышел на проект, а все как-то никак. Нет, из под него какой-то код конечно же получается, но ни продуктивности, ни лаконичности, ни красоты, чтоли. И спорите с ним до хрипоты, о том, что можно-таки быстрее. В разы быстрее. Вопрос в том, как?.. <br><br>
Потом его увольняешь. Не сработались. Но свято место пусто не бывает - вскоре выходит замена. Очень быстро становится понятно, талант. Инженер в лице которого ты видишь ту самую продуктивность, о которой холиварил с коллегами ранее. Продуктивность, которой могут позавидовать многие Senior. А ведь речь идет про Middle. <br><br>
Что отличает их? Что дает одному инженеру возможность оторваться от земли оставив остальных с мыслью "это глюк, так не бывает"? Любознательность. Тяга к Engineering Excellence. Вера в то, что можо еще быстрее и при этом одновременно и качественнее. Вопрос в том, как?.. <br><br>
А знаешь, что его волнует каждый день? Он часто говорит: "я переживаю, потому что еще не достаточно разогнался".<br><br>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtsCdG90bsd9Sl1EDe29LblOr03KjlgW-Cg-SSmO8o62RAN1xZ9aQvQF-PeWG0O9XX2vWlQiNsEqOIQOXdpx54idpcfr2FC9MZU-cbWzYXSgcrRYvSX2ozkX2pHeDmT3-tpizD3JYwbd47/s1550/fly.jpg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="400" data-original-height="1080" data-original-width="1550" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtsCdG90bsd9Sl1EDe29LblOr03KjlgW-Cg-SSmO8o62RAN1xZ9aQvQF-PeWG0O9XX2vWlQiNsEqOIQOXdpx54idpcfr2FC9MZU-cbWzYXSgcrRYvSX2ozkX2pHeDmT3-tpizD3JYwbd47/s400/fly.jpg"/></a></div>
Немногно истории<br><br>
<center><iframe width="560" height="315" src="https://www.youtube.com/embed/W6x4sVWlydY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></center>
А вот Братья Райт нашего времени
<center><iframe width="560" height="315" src="https://www.youtube.com/embed/HUqrw_MRXPA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></center>
Еще столько всего предстоит открыть!А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-26074483746894310782021-06-07T23:54:00.005+03:002021-06-07T23:54:58.515+03:00Если много сабрепозиториев, ленька вводить для каждого пароль и не хочется sshКоманда в помощь под windows
<pre>git config --global credential.helper wincred</pre>
Под Linux
<pre>git config --global credential.helper cache</pre>
Отключаем
<pre>git config --global --unset credential.helper</pre>
Проверяем что отключили
<pre>git config --global credential.helper</pre>
А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-60515202058580958802021-06-07T22:48:00.006+03:002021-06-07T22:48:59.827+03:00Если ошибся с submodules и добавил его как git@ а не https://Поможет чудо скрипт (на примере codenjoy)
<pre>git submodule deinit CodingDojo/games/engine
git rm -f CodingDojo/games/engine
rm -rf .git/modules/CodingDojo/games/engine
git commit -m "Deleted submodule CodingDojo/games/engine"
git submodule add --force https://github.com/codenjoyme/codenjoy-engine.git CodingDojo/games/engine
git commit -m "Add submodule CodingDojo/games/engine"</pre>
Что значит - удалить все, и добавить по-новомуА Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-68208769525620628152021-01-29T18:11:00.000+02:002021-01-29T18:11:06.438+02:00Запускаем Docker в Docker<p>У меня появилась такая задача. Основное приложение codenjoy состоит из компонентов. Каждый компонент-процесс на хостовой машине завернут в Docker. Всеми контейнерами управляет DockerСompose. Один из компонентов - nginx является фасадом, выглядывает наружу, перенаправляя все запросы внутри виртуальной сети созданной DockerCompose.<br /></p><p>На сцене появляется новый компонент-процесс, который запускает другие процессы внутри своего Docker управляя им. А раз уж оно само завенрнуто в Docker, то отсюда вывод google('ho to run docker inside docker'). </p><p> Я понимал что эта идея бредовая и очень пахнет, но мне было интересно как решают подобные проблемы. И решение нашлось. Вовсе не обязательно запускать докер в докере, хоть и можно. Лучше взять докер хостовой машины и дасть доступ к нему изнутри другого докера. Для этого надо подмаунтить вот такую штуковину </p><blockquote><pre style="background-color: white; color: black; font-family: "Courier New", monospace; font-size: 12pt;">/var/run/docker.sock:/var/run/docker.sock:rw</pre></blockquote><p>Сделать это можно либо через ключик -v в команде docker run, либо в блоке volumes docker-compose.yml файла.</p><blockquote><pre style="background-color: white; color: black; font-family: "Courier New", monospace; font-size: 12pt;">docker run -v /var/run/docker.sock:/var/run/docker.sock -ti ubuntu bash <br />curl -sSL <a href="https://get.docker.com/" rel="nofollow" target="_blank">https://get.docker.com/</a> | sh<br />docker ps</pre></blockquote><p>Сам же Dockerfile для запуска из под docker-compose.yml у меня такой</p><blockquote><pre style="background-color: white; color: black; font-family: "Courier New", monospace; font-size: 12pt;">FROM java-workspace as dockerized-java-workspace<br /><br />USER root<br />ENV DEBIAN_FRONTEND noninteractive<br /><br />RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \<br /> && apt-key fingerprint 0EBFCD88 \<br /> && apt-get -y install software-properties-common \<br /> && add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(dpkg --status tzdata|grep Provides|cut -f2 -d'-') stable" \<br /> && apt-get -y update \<br /> && apt-get -y install docker-ce \<br /> && docker -v</pre></blockquote><p>Тут java-workspace наследуется в конечном счете от openjdk:11-jdk, т.к. мне нужна еще java на борту.</p><p>Детально можно <a href="https://github.com/codenjoyme/codenjoy-portable-linux/commit/c9f0c337e7c184380909e0130a4237de86ee628c">глянуть в этом коммите</a>.</p><p><br /></p>А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com0tag:blogger.com,1999:blog-7317504408463627049.post-54820040597363764422020-03-16T02:03:00.001+02:002021-09-18T19:56:10.084+03:00SmartAssert или как проверять статус всех assertEquals в одном тесте<div dir="ltr" style="text-align: left;" trbidi="on">
Решение этого <a href="https://gist.github.com/cb372/2419626?fbclid=IwAR0EvVhxmWbmDpnhSp7aFSbdQTNs5Jxdvrn_o5X01ZHlJNFYhbAQrSPCM8U">вопроса есть с коробки</a>, я же хотел покодить в свое удовольствие и делюсь тем, что получилось.<br /><br />
Код <a href="https://github.com/codenjoyme/codenjoy/blob/472f5ce4c4ec9b313369a9f90222373f431197c1/CodingDojo/server/src/test/java/com/codenjoy/dojo/stuff/SmartAssert.java">лежит тут</a>. Лицензия GNU GPL v3. На здоровье.<br />
<br />
Если тебе лень писать @Test public void shouldBlahBlahBlah_whenBlahBlahBlah () {} для каждого отдельного ассерта, то скорее всего у тебя несколько ассертов в одном тесте. Так не рекомендуют делать, но это решение встречается достаточно часто. Почему? Просто потому что блок // given в этом конкретном тесте с 3-5-7ю ассертами может быть большим. А копипастить его в каждый отдельный тест чтобы уютно разместить там 1 assertEquals еще большее зло. <br />
<br />
Да, конечно можно выделить // given часть теста в отдельный метод и повторно использовать. Только фигня получится. Вот был лаконичный тест: в // given подготовились, в // when вызвали одну строчку тестируемого метода, и в 3-5-7ю assertEquals проверили объект, который вернулся. И что? 1 тест. И следуя рекомендации 1 assert на 1 тест мы получаем кучу операторных скобок, 8 методов и 10 шагов в сторону от рекомендации - "тест как документация". Вот все было перед глазами, а теперь иди собирай все все мысли по классу.<br />
<br />
И если ты не следуешь бездумно всем рекомендациям, описанным в инженерных книгах, то ты, так же как и я получишь один тест с 3-5-7ю ассертами. Но тут есть другой бок - скорее всего ты, так же как и я, утомился перезапускать этот один стройный тест. Да-да, интеграционный, блин, со спрингом под капотом, от чего он ранится не 10 милисекунд, а 100 секунд! Потому что мир джава жесток и беспощаден...<br />
<pre style="background-color: white; font-family: Consolas; font-size: 12pt;"><span style="color: olive;">@SpringBootTest</span>(classes = CodenjoyContestApplication.<span style="color: navy; font-weight: bold;">class</span>,
properties = <span style="color: green; font-weight: bold;">"spring.main.allow-bean-definition-overriding=true"</span>)
<span style="color: olive;">@RunWith</span>(SpringRunner.<span style="color: navy; font-weight: bold;">class</span>)
<span style="color: olive;">@ActiveProfiles</span>(<span style="color: olive;">SQLiteProfile</span>.<span style="color: #660e7a; font-style: italic; font-weight: bold;">NAME</span>)
<span style="color: olive;">@Import</span>(RestGameControllerTest.ContextConfiguration.<span style="color: navy; font-weight: bold;">class</span>)
<span style="color: olive;">@WebAppConfiguration</span>
<span style="color: navy; font-weight: bold;">public class </span>RestGameControllerTest {</pre>
...в этом тесте будет несколько assertEquals. И раз за разом, натыкаясь на очередной свалившийся ассерт, скорее всего ты, так же как и я, задавался вопросом: какого фига в junit тест валится после первого же ассерта? <br />
<br />
Решение созрело.<br />
<br />
Да, я знаю, что писать несколько assertEquals в одном тесте (было) не ок. Но писать по новой конструкции для каждого ассерта тоже перебор.<br />
<pre style="font-family: Consolas; font-size: 11pt;"><span style="color: olive;">@Test</span>
<span style="color: navy; font-weight: bold;">public void </span>shouldBlahBlah_whenBlahBlah() {
...
}</pre>
Вот и написал свой assertEquals который агрегирует ошибки и валидирует их только, когда ты явно этого попросишь в @After методе
<br />
<pre style="font-family: Consolas; font-size: 11pt;"><span style="color: olive;">@After</span>
<span style="color: navy; font-weight: bold;">public void </span>checkErrors() {
SmartAssert.<span style="font-style: italic;">checkResult</span>();
}</pre>
В простом вариаенте запуска потребуется еще всего лишь импортнуть класс, в котором есть статический assertEquals метод.
<br />
<pre style="font-family: Consolas; font-size: 11pt;"><span style="color: navy; font-weight: bold;">import static </span>com.codenjoy.dojo.stuff.SmartAssert.*;</pre>
А старый импорт удалить
<br />
<pre style="font-family: Consolas; font-size: 11pt;"><span style="color: navy; font-weight: bold;">import static </span>org.junit.Assert.*;</pre>А что умного? Так это вывод результатов. Каждый тест отработает от начала и до конца, не важно сколько assertEquals по дороге не пройдут. При этом ты увидишь в консоли все expected: <qwe> but was: <asd> блоки и кусочек стектрейса из тестового класса, в котором были вызовы измененного assertEquals длинной до 10 строк (на это можно повлиять).<br />
<br />
Например вот тест, я в нем заведомо поломал первые 4 assert, заменив expected с true на false
<br />
<pre style="font-family: Consolas; font-size: 11pt;"><span style="color: olive;">@Test</span>
<span style="color: navy; font-weight: bold;">public void </span>shouldExists() {
<span style="font-style: italic;">assertEquals</span>(<span style="color: navy; font-weight: bold;">false</span>, <span style="color: #660e7a; font-weight: bold;">service</span>.exists(<span style="color: green; font-weight: bold;">"first"</span>));
<span style="font-style: italic;">assertEquals</span>(<span style="color: green; font-weight: bold;">"false"</span>, get(<span style="color: green; font-weight: bold;">"/rest/game/first/exists"</span>));
<span style="font-style: italic;">assertEquals</span>(<span style="color: navy; font-weight: bold;">false</span>, <span style="color: #660e7a; font-weight: bold;">service</span>.exists(<span style="color: green; font-weight: bold;">"second"</span>));
<span style="font-style: italic;">assertEquals</span>(<span style="color: green; font-weight: bold;">"false"</span>, get(<span style="color: green; font-weight: bold;">"/rest/game/second/exists"</span>));
<span style="font-style: italic;">assertEquals</span>(<span style="color: navy; font-weight: bold;">false</span>, <span style="color: #660e7a; font-weight: bold;">service</span>.exists(<span style="color: green; font-weight: bold;">"non-exists"</span>));
<span style="font-style: italic;">assertEquals</span>(<span style="color: green; font-weight: bold;">"false"</span>, get(<span style="color: green; font-weight: bold;">"/rest/game/non-exists/exists"</span>));
}</pre>
В оригинальной версии тест прекратил бы выполнение после первой же строчки. Слетел ассерт - давай до свидания. Но не co SmartAssert
<br />
<pre><span style="color: olive; font-family: "consolas"; font-size: 11pt;">org.junit.ComparisonFailure: expected:<[fals]e>; but was:<[tru]e>
com.codenjoy.dojo.web.rest.RestGameControllerTest.shouldExists(RestGameControllerTest.java:135)
org.junit.ComparisonFailure: expected:<[fals]e> but was:<[tru]e>
com.codenjoy.dojo.web.rest.RestGameControllerTest.shouldExists(RestGameControllerTest.java:136)
org.junit.ComparisonFailure: expected:<[fals]e> but was:<[tru]e>
com.codenjoy.dojo.web.rest.RestGameControllerTest.shouldExists(RestGameControllerTest.java:138)
org.junit.ComparisonFailure: expected:<[fals]e> but was:<[tru]e>
com.codenjoy.dojo.web.rest.RestGameControllerTest.shouldExists(RestGameControllerTest.java:139)
java.lang.AssertionError: There are errors
at org.junit.Assert.fail(Assert.java:88)
at com.codenjoy.dojo.stuff.SmartAssert.checkResult(SmartAssert.java:118)
at com.codenjoy.dojo.stuff.SmartAssert.checkResult(SmartAssert.java:132)
at com.codenjoy.dojo.web.rest.RestGameControllerTest.checkErrors(RestGameControllerTest.java:98)</span></pre>
Важно, что ссылки на сами ассерты в коде в idea кликабельны.
<br />
<pre style="font-family: Consolas; font-size: 11pt;"><span style="color: olive;">(RestGameControllerTest.java:139)</span></pre>
Так же я добился замены базовым проверятором Idea констукции<br />
<pre><span style="color: olive; font-family: "consolas"; font-size: 11pt; white-space: pre-wrap;">expected:<[fals]e> but was:<[tru]e></span></pre>На такую, которая будет подсвечивать diff каждого слетевшего ассерта отдельно. В этом мне помог deprecated MultipleFailureException.</div><div dir="ltr" style="text-align: left;" trbidi="on"><br />Код <a href="https://github.com/codenjoyme/codenjoy/blob/653e3d37582f993da4eeb04661a2bce7efa7998a/CodingDojo/server/src/test/java/com/codenjoy/dojo/stuff/SmartAssert.java">лежит тут</a>. Лицензия GNU GPL v3. На здоровье.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg__4VBPAUBOQdLN4kpqoR0TE7noW7Ua7A61RuMBYZlAP2AzU-JKTBamP5t870RXVZHYokiAUZeLM0yi3h68dyLB1E9mr0AgL9XAhsi2DmUgHzWxM6fmqeJK7uMPJMFoQu6B_M6vo7veM8g/s1600/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1366" height="340" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg__4VBPAUBOQdLN4kpqoR0TE7noW7Ua7A61RuMBYZlAP2AzU-JKTBamP5t870RXVZHYokiAUZeLM0yi3h68dyLB1E9mr0AgL9XAhsi2DmUgHzWxM6fmqeJK7uMPJMFoQu6B_M6vo7veM8g/s640/1.png" width="640" /></a></div>
<br /></div>
А Пофиг!http://www.blogger.com/profile/17396129752484594405noreply@blogger.com1