Что вы обычно понимаете под термином «технический долг»? Наверное, всякие проблемы в архитектуре, низкое качество кода, плохое покрытие тестами и т.д. И тут как с кредитом – если его не гасить ежемесячно, то будут начисляться штрафы, потом у вас отнимут машину и квартиру, и в конце вы станете банкротом. Причём даже одна одноразовая несвоевременная уплата процента – уже неприятная проблема (кто сталкивался с таким в жизни, хорошо это представляет). Точно так же и с программным проектом – если не гасить техдолг непрерывно, каждый день, с каждым коммитом, то ваш проект будет медленно тонуть и в конце концов помрёт. Да, я убеждён, что гасить долг нужно именно непрерывно. Другие стратегии обречены на провал. Например, «давайте 4 месяца кодить, а потом месяц гасить техдолг» – так не получится, что подтверждено многолетним опытом индустрии.
С архитектурными проблемами и плохим кодом в целом всё понятно. Видите косяк в коде – исправляете. Видите архитектурную проблему – рефакторите. Мало тестов – дописываете новые. Все эти проблемы плавают на поверхности. Но вот есть одна проблема, которая есть, но она далеко не очевидна (по крайней мере не всем). Это то, что вы используете устаревший стек технологий. Это версии библиотек, фреймворков, JDK, OS и т.д. Они очень быстро устаревают. Ну как быстро: если вы зафиксировали версии в pom.xml, и не обновляли их год, то обновиться вы сможете более-менее безболезненно. Если не обновляете два года, то уже придётся попотеть. Три года и более – придётся очень серьёзно так поработать. Вот недавно мы обновили наш стек технологий до последнего, который не обновляли 4 года. Так эту задачу делал я целых 3 месяца!
Старые версии – это точно такой же техдолг, как и плохой код. И я утверждаю, что обновлять версии тоже нужно постоянно и непрерывно. Почему – потому что при обновлении библиотек в любом случае будет что-то ломаться. Что лучше: обновить одну библиотеку и починить только то, что сломалось в ней (исчезло, стало deprecated) или обновить 100 библиотек единовременно, и засесть на месяц, чтобы исправить всё? Ответ очевиден.
Объясню на примере Java. Всё-таки блог про неё.
Вот сидите вы на Java 8 и чувствуете себя превосходно. А тем временем выходит Java 9, 10, 11, 12, 13, 14, 15, 16 и вот уже на подходе 17. Вы думаете: «ну вот 17 выйдет, и тогда, наверное, уже пора обновляться». Поверьте, вы задолбаетесь. Кто переходил с Java 8 на 11, тот меня прекрасно понимает. А вот 8 → 17 это будет прям сущий ад. Вам придётся исправить просто тонну всего. Вот смотрите, что крупного изменилось в версиях Java:
• Java 9: модули, поменялась схема версионирования, исчезли многие проприетарные классы (sun.*), а на те, что остались, теперь нельзя ссылаться в compile-time.
• Java 11: нельзя запускать аплеты, Web Start, удален JavaFX, JAX-WS, JAXB, JAF, CORBA, JTA.
• Java 12: больше нельзя компилировать в Java 6.
• Java 14: удалён CMS, удалены инструменты pack200 и unpack200.
• Java 15: удалён Nashorn.
• Java 16: внутренности JDK стали закрытыми по умолчанию.
• Java 17: внутренности JDK теперь нельзя открыть одним флагом.
А сколько там мелких изменений? Тысячи. Новые предупреждения, куча deprecated API, поведения некоторых методов, кодировки, многих флагов JVM нет или изменились на другие дефолтные значения. И про новые баги в JDK тоже не стоит забывать. И т.д. и т.п.
А ещё при обновлении JDK, скорее всего, придётся обновить и версии библиотек/фреймворков, потому что многие из них просто перестанут работать при апгрейде. Также придётся обновить системы сборки, IDE. Возможно придётся обновить application сервера, докеры и кубернетесы. А может быть даже операционную систему. А потом ещё в течение нескольких месяцев будут постоянно всплывать баги, которые возникли из-за обновления, потому что вы в любом случае не сможете протестировать абсолютно всё. Представили себе весь этот ужас? В общем, работа на несколько месяцев вам гарантированно обеспечена.
#Java11 #Java17
С архитектурными проблемами и плохим кодом в целом всё понятно. Видите косяк в коде – исправляете. Видите архитектурную проблему – рефакторите. Мало тестов – дописываете новые. Все эти проблемы плавают на поверхности. Но вот есть одна проблема, которая есть, но она далеко не очевидна (по крайней мере не всем). Это то, что вы используете устаревший стек технологий. Это версии библиотек, фреймворков, JDK, OS и т.д. Они очень быстро устаревают. Ну как быстро: если вы зафиксировали версии в pom.xml, и не обновляли их год, то обновиться вы сможете более-менее безболезненно. Если не обновляете два года, то уже придётся попотеть. Три года и более – придётся очень серьёзно так поработать. Вот недавно мы обновили наш стек технологий до последнего, который не обновляли 4 года. Так эту задачу делал я целых 3 месяца!
Старые версии – это точно такой же техдолг, как и плохой код. И я утверждаю, что обновлять версии тоже нужно постоянно и непрерывно. Почему – потому что при обновлении библиотек в любом случае будет что-то ломаться. Что лучше: обновить одну библиотеку и починить только то, что сломалось в ней (исчезло, стало deprecated) или обновить 100 библиотек единовременно, и засесть на месяц, чтобы исправить всё? Ответ очевиден.
Объясню на примере Java. Всё-таки блог про неё.
Вот сидите вы на Java 8 и чувствуете себя превосходно. А тем временем выходит Java 9, 10, 11, 12, 13, 14, 15, 16 и вот уже на подходе 17. Вы думаете: «ну вот 17 выйдет, и тогда, наверное, уже пора обновляться». Поверьте, вы задолбаетесь. Кто переходил с Java 8 на 11, тот меня прекрасно понимает. А вот 8 → 17 это будет прям сущий ад. Вам придётся исправить просто тонну всего. Вот смотрите, что крупного изменилось в версиях Java:
• Java 9: модули, поменялась схема версионирования, исчезли многие проприетарные классы (sun.*), а на те, что остались, теперь нельзя ссылаться в compile-time.
• Java 11: нельзя запускать аплеты, Web Start, удален JavaFX, JAX-WS, JAXB, JAF, CORBA, JTA.
• Java 12: больше нельзя компилировать в Java 6.
• Java 14: удалён CMS, удалены инструменты pack200 и unpack200.
• Java 15: удалён Nashorn.
• Java 16: внутренности JDK стали закрытыми по умолчанию.
• Java 17: внутренности JDK теперь нельзя открыть одним флагом.
А сколько там мелких изменений? Тысячи. Новые предупреждения, куча deprecated API, поведения некоторых методов, кодировки, многих флагов JVM нет или изменились на другие дефолтные значения. И про новые баги в JDK тоже не стоит забывать. И т.д. и т.п.
А ещё при обновлении JDK, скорее всего, придётся обновить и версии библиотек/фреймворков, потому что многие из них просто перестанут работать при апгрейде. Также придётся обновить системы сборки, IDE. Возможно придётся обновить application сервера, докеры и кубернетесы. А может быть даже операционную систему. А потом ещё в течение нескольких месяцев будут постоянно всплывать баги, которые возникли из-за обновления, потому что вы в любом случае не сможете протестировать абсолютно всё. Представили себе весь этот ужас? В общем, работа на несколько месяцев вам гарантированно обеспечена.
#Java11 #Java17
Опрос только для работающих программистов из России. Какая у вас зарплата? Чистыми, на руки, в месяц, в рублях.
Final Results
0%
0-29 999
2%
30 000-49 999
2%
50 000-69 999
4%
70 000-99 999
8%
100 000-129 999
11%
130 000-169 999
11%
170 000-209 999
8%
210 000-249 999
12%
250 000+
43%
Не из России / не работаю
| Welcome to JShell -- Version 16
| For an introduction type: /help intro
jshell> import java.lang.reflect.Field;
jshell> import sun.misc.Unsafe;
jshell> // Поля в record являются truly final
jshell> record Point(int x) {}
| created record Point
jshell> Point p = new Point(1);
p ==> Point[x=1]
jshell> // Попробуем заменить p.x на 42
jshell> Field field = Point.class.getDeclaredField("x");
field ==> private final int Point.x
jshell> field.setAccessible(true);
jshell> field.set(p, 42);
| Exception java.lang.IllegalAccessException: Can not set final int field REPL.$JShell$13$Point.x to java.lang.Integer
| at UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException (UnsafeFieldAccessorImpl.java:76)
| at UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException (UnsafeFieldAccessorImpl.java:80)
| at UnsafeQualifiedIntegerFieldAccessorImpl.set (UnsafeQualifiedIntegerFieldAccessorImpl.java:79)
| at Field.set (Field.java:793)
| at (#7:1)
jshell> // Не сработало. Может, можно заменить через Unsafe?
jshell> Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe ==> private static final sun.misc.Unsafe sun.misc.Unsafe.theUnsafe
jshell> theUnsafe.setAccessible(true);
jshell> Unsafe unsafe = (Unsafe) theUnsafe.get(null);
unsafe ==> sun.misc.Unsafe@6073f712
jshell> // Сначала нужно узнать offset поля Point.x
jshell> long offset = unsafe.objectFieldOffset(field);
| Exception java.lang.UnsupportedOperationException: can't get field offset on a record class: private final int REPL.$JShell$13$Point.x
| at Unsafe.objectFieldOffset (Unsafe.java:648)
| at (#11:1)
jshell> // Тоже не сработало. Это тоже учли. Но зачем нам offset, ведь мы и так знаем, что он равен 12?
jshell> unsafe.putIntVolatile(p, 12, 42);
jshell> p
p ==> Point[x=42]
jshell> // Сработало!👍1
Как-то незаметно вышла JDK 16.0.1. Почти никто не написал. Вот, исправляю:
https://www.oracle.com/java/technologies/javase/16-0-1-relnotes.html
http://jdk.java.net/16/
#java16
https://www.oracle.com/java/technologies/javase/16-0-1-relnotes.html
http://jdk.java.net/16/
#java16
Сколько у вас репутации на StackOverflow?
Final Results
43%
Нет аккаунта
29%
< 10
8%
[10, 100)
9%
[100, 500)
6%
[500, 2k)
4%
[2k, 10k)
0%
[10k, 20k)
1%
[20k, 50k)
0%
≥ 50k
Серьёзная история в сообществе Scala. Спустя три года, молодая Scala-энтузиастка объявила о домогательствах со стороны Джона Претти, одного из центральных фигур Scala-сообщества.
UPD: другая участница сообщества подтверждает историю Ифань и описывает похожий опыт.
UPD: другая участница сообщества подтверждает историю Ифань и описывает похожий опыт.
35+ членов Scala-сообщества подписали открытое письмо, где объявили, что:
• Будут бойкотировать компании, которые предоставляют услуги Претти, не посещая их мероприятия и не пользуясь их продуктами и услугами
• Не будут принимать участия в конференциях, в которых участвует Претти
• Не будут принимать участия в других конференциях, где нет работающего code of conduct
• Не будут использовать и продвигать программные артефакты, которые поддерживаются Претти
Вот и всё. Менее чем за час человека отменили. Полностью.
#scala
• Будут бойкотировать компании, которые предоставляют услуги Претти, не посещая их мероприятия и не пользуясь их продуктами и услугами
• Не будут принимать участия в конференциях, в которых участвует Претти
• Не будут принимать участия в других конференциях, где нет работающего code of conduct
• Не будут использовать и продвигать программные артефакты, которые поддерживаются Претти
Вот и всё. Менее чем за час человека отменили. Полностью.
#scala
scala-open-letter
Open letter of support for community members targeted by Jon Pretty
We, the undersigned, have become aware that, for some time, Jon Pretty has abused his position of privilege and stature within the Scala community to sexually harass and victimize women. Our purpose in releasing this open letter is to bring attention to the…
В проекте Lilliput (попытки сжать заголовки объектов до 32/64 бит вместо 96/128) помаленьку начинает происходить активность. Вот уже отключён Biased Locking и identity hash code уменьшен с 31 до 25 бит.
Много лет использую схему именования переменных типа Map в Java. Схема очень простая: xToYMap. То есть что (X) мапится во что (Y). Например:
Entry по такой схеме логично именуются как xToY:
Ещё пример:
Если в примере выше там был Point, и из типа Value худо-бедно можно было понять, что он означает, то здесь Integer может означать что угодно (возраст? вес? зарплата?).
В общем, рекомендую такую схему. Использую много лет.
Map<String, Point> locations;или
Map<String, Point> idToLocationMap;Какой вариант понятнее читателю? Вопрос риторический. Особенно это стало критичным, когда появились var'ы:
var locations = getLocations();или
var idToLocationMap = getIdToLocationMap();Тут первый вариант уж совсем плох.
Entry по такой схеме логично именуются как xToY:
for (var idToLocation : idToLocationMap.entrySet()) {
...
}
Тут немедленно понятно, что такое idToLocation.Ещё пример:
Map<String, Integer> personNameToAgeMap;Если в примере выше там был Point, и из типа Value худо-бедно можно было понять, что он означает, то здесь Integer может означать что угодно (возраст? вес? зарплата?).
В общем, рекомендую такую схему. Использую много лет.
Мда, ребят, половина не читала про concurrency. Как вас вообще допустили к написанию кода? Как прошли собеседование? Ну-ка бегом читать! :)
Книга, кстати, несмотря на то, что вышла в 2006, не устарела вообще нисколько, так что страх, что вы прочитаете и усвоите неактуальные вещи, беспочвенен. Книга про фундаментальные концепции, которые будут актуальны всегда.
Книга, кстати, несмотря на то, что вышла в 2006, не устарела вообще нисколько, так что страх, что вы прочитаете и усвоите неактуальные вещи, беспочвенен. Книга про фундаментальные концепции, которые будут актуальны всегда.
Ребята, тут возник спор у нас, поэтому более уточняющий опрос (чтобы не задевать чувства)
Final Results
23%
Я читал JCiP
11%
Я читал какую-то другую книгу по concurrency в Java и читал JCiP
3%
Я читал какую-то другую книгу по concurrency в Java, но не читал JCiP
21%
Читал только книги по Java, где concurrency посвящены несколько глав, но не вся книга
36%
Читал про concurrency в других источниках (статьи, доклады и т.п.)
7%
Вообще не читал про concurrency никогда
«Не нанимайте токсичных людей» – постоянно я вижу в советах по рекрутингу. А, собственно, почему?
Не, я не спорю, что токсичность – это очень плохо. И я даже не стану спорить с тем, что софт-скиллы не менее важны, чем хард-скиллы. Но когда речь идёт о выборе между кандидатом с хорошим софт-скиллами, но слабыми техническими навыками и хорошим специалистом, но с плохими софт-скиллами, почему-то современная школа рекрутинга выбирает первых, а на вторых ставит крест. Типа, слабого кандидата мы всегда подтянем в технической части, а вот человека с плохим характером уже не исправить.
А с чего вы, собственно, это взяли? Скиллы (навыки) – на то они и скиллы, что их можно развить и улучшить. Человек не рождается мудаком, он становится таковым под действием внешних факторов. Может, сначала стоит разобраться, почему у человека проблемы с взаимодействием с другими людьми? Не просто так он агрессивен. Как правило, если человек грубо и пренебрежительно ведёт себя с другими людьми, то, скорее всего, он несчастен в глубине души. Может, его родители в детстве били? Может, в школе унижали, и он поэтому во всех остальных видит врагов? Или, может, он с мамой в ссоре, и вымещает всю злость на коллегах?
Если человек крутой спец, то почему бы не дать ему хотя бы шанс? Нанять в штат психолога, и пусть он работает с такими людьми. Большинство крупных и даже средних контор может себе это позволить. У каждой психологической травмы есть причина, и, выяснив её, человека можно вылечить. А потом получить крутого человека в команде. И просто хорошего человека в обществе. Конечно, не все будут проходить испытательный срок, и если у человека не будет никакой положительной тенденции, то тут уж сорян: мы сделали всё, что смогли. Тут всё так же как и с хард-скиллами: человек должен сам хотеть идти навстречу и прокачивать свои навыки.
Я уверен, что большинство мудаков можно исправить. Нужно только захотеть.
Не, я не спорю, что токсичность – это очень плохо. И я даже не стану спорить с тем, что софт-скиллы не менее важны, чем хард-скиллы. Но когда речь идёт о выборе между кандидатом с хорошим софт-скиллами, но слабыми техническими навыками и хорошим специалистом, но с плохими софт-скиллами, почему-то современная школа рекрутинга выбирает первых, а на вторых ставит крест. Типа, слабого кандидата мы всегда подтянем в технической части, а вот человека с плохим характером уже не исправить.
А с чего вы, собственно, это взяли? Скиллы (навыки) – на то они и скиллы, что их можно развить и улучшить. Человек не рождается мудаком, он становится таковым под действием внешних факторов. Может, сначала стоит разобраться, почему у человека проблемы с взаимодействием с другими людьми? Не просто так он агрессивен. Как правило, если человек грубо и пренебрежительно ведёт себя с другими людьми, то, скорее всего, он несчастен в глубине души. Может, его родители в детстве били? Может, в школе унижали, и он поэтому во всех остальных видит врагов? Или, может, он с мамой в ссоре, и вымещает всю злость на коллегах?
Если человек крутой спец, то почему бы не дать ему хотя бы шанс? Нанять в штат психолога, и пусть он работает с такими людьми. Большинство крупных и даже средних контор может себе это позволить. У каждой психологической травмы есть причина, и, выяснив её, человека можно вылечить. А потом получить крутого человека в команде. И просто хорошего человека в обществе. Конечно, не все будут проходить испытательный срок, и если у человека не будет никакой положительной тенденции, то тут уж сорян: мы сделали всё, что смогли. Тут всё так же как и с хард-скиллами: человек должен сам хотеть идти навстречу и прокачивать свои навыки.
Я уверен, что большинство мудаков можно исправить. Нужно только захотеть.
Кстати, пользуясь случаем, рекомендую посмотреть шикарный сериал "Триггер". Конечно, он имеет мало общего с реальными ("научными") методами лечения психическим болезней, но я из него почерпнул одну важную идею. А именно тому, что даже если человек абсолютная мразь, то не надо преждевременно ставить на нём крест. Возможно если попытаться разобраться, почему он стал мразью, то может получится его исправить. Меня, например, очень сильно поразила сцена, где ФСИНовец жестоко и по беспределу избивал в тюрьме главного героя, но тот не только не затаил на него злобу, а пригласил его на сеанс и смог помочь ему, разобравшись в его психологической проблеме.
В общем, этого я и боялся. Я ожидал, что тот факт, что Java 17 будет LTS, будет сдерживающим фактором не пихать туда preview-фичи, но нет. Они всё-таки хотят засунуть JEP 406 в релиз.
Чем это плохо? Это значит, что IDE будут обязаны поддерживать вот эту предварительную версию Pattern Matching for switch много лет. И при этом одновременно поддерживать стабильную версию, которая выйдет где-то в районе Java 19. Поддерживать две версии одной фичи много лет – прям совсем плохо. Дай бог, если они не будут сильно отличаться.
#Java17
Чем это плохо? Это значит, что IDE будут обязаны поддерживать вот эту предварительную версию Pattern Matching for switch много лет. И при этом одновременно поддерживать стабильную версию, которая выйдет где-то в районе Java 19. Поддерживать две версии одной фичи много лет – прям совсем плохо. Дай бог, если они не будут сильно отличаться.
#Java17
У вас есть в проекте какие-нибудь утилитные методы, которых вам не хватает в стандартной библиотеке? Меня вот задалбливает каждый раз писать:
Ещё несколько методов:
for (int i = 0; i < list.size(); i++) {
// action using i and list
}
Поэтому я завёл себе метод:public static <A> void forEachWithIndex(Iterable<A> iterable, ObjIntConsumer<A> consumer) {
int i = 0;
for (A value : iterable) {
consumer.accept(value, i++);
}
}
Теперь можно писать так:forEachWithIndex(list, <action>);
К тому же работает не только со списками, а с любым Iterable.Ещё несколько методов:
public static <A, B> List<B> mapList(List<A> list, Function<A, B> f);
public static <A, B> List<B> mapListWithIndex(List<A> list, ObjIntBiFunction<A, B> f); // Пришлось завести ObjIntBiFunction
public static <T> List<T> flattenList(List<? extends List<T>> list);
А у вас есть что-то подобное? Пишите примеры в комментариях.