Всем привет!
Хочу рассказать про ряд неочевидных особенностей enum в Java.
Поехали!
1) enum - это полноценный класс, у него могут быть поля, методы, обычные и статические
2) любой enum неявно (!) расширяет абстрактный класс Enum, поэтому наследовать enum от кого-то другого нельзя. Например, один enum от другого. При этом добавить final для enum нельзя, т.к. он уже фактически final)
3) зато enum может реализовывать интерфейсы, как и любой другой класс в Java
4) самое интересное - каждое значение enum является наследником самого enum. Т.е. можно объявить метод show в enum Direction и переопределить его Direction.LEFT. На первый взгляд противоречит пункту 2, но см. дальше
5) Как устроен enum внутри?
Возьмем такой enum:
enum Direction {
LEFT {
@Override
void show() {}
},
RIGTH;
void show() {}
}
Если посмотреть его байткод, упросить его и мысленно декомпилировать обратно в Java, то получится:
public class Direction extends Enum {
public static final Direction LEFT = new Direction() {
@Override
void show() {
}
};
public static final Direction RIGTH = new Direction();
private static final Direction[] VALUES = values();
private Direction(String name, int ordinal) {
super(name, ordinal);
}
public static Direction[] values() {
return VALUES.clone();
}
public static Direction valueOf(String name) {
return Enum.valueOf(Direction.class, name);
}
void show() {
}
}
Т.е. enum превращается в класс со статическими public полями. И у класса создаются анонимные классы-наследники.
Собственно, если бы в Java не было enum - я бы реализовал перечисление примерно также
6) enum не может иметь public конструктор, и метод clone для него возвращает CloneNotSupportedException. И как я говорил выше он фактически финальный. Следовательно, во время работы программы число объектов enum равно число значений enum
7) из-за этого с помощью enum легко можно реализовать singleton. Можно, но не нужно, если вы используете Spring или другой IoC контейнер) Но об этом как-нибудь позже
8) исходя из сказанного выше enum-ы можно и нужно сравнивать по ==. Во-первых это нагляднее, а во-вторых меньше шансов получить NPE неправильно написав код сравнения.
9) хочу обратить внимание на классы EnumSet и EnumMap, которые эффективно с точки зрения расхода памяти позволяют использовать значения Enum в качестве ключей. Enum реализует Comparable, сравнение идет по полю ordinal - порядковому номеру значения.
10) Enum реализует Serializible и успешно сериализуется. Но есть одна важная особенность. При сериализации конкретного объекта сохраняется лишь его имя, и объект восстанавливается потом вызовом valueOf. Значения других полей теряются. Следовательно, enum должен быть реализован иммутабельным, значения всех полей должны задаваться в конструкторе.
#java
Хочу рассказать про ряд неочевидных особенностей enum в Java.
Поехали!
1) enum - это полноценный класс, у него могут быть поля, методы, обычные и статические
2) любой enum неявно (!) расширяет абстрактный класс Enum, поэтому наследовать enum от кого-то другого нельзя. Например, один enum от другого. При этом добавить final для enum нельзя, т.к. он уже фактически final)
3) зато enum может реализовывать интерфейсы, как и любой другой класс в Java
4) самое интересное - каждое значение enum является наследником самого enum. Т.е. можно объявить метод show в enum Direction и переопределить его Direction.LEFT. На первый взгляд противоречит пункту 2, но см. дальше
5) Как устроен enum внутри?
Возьмем такой enum:
enum Direction {
LEFT {
@Override
void show() {}
},
RIGTH;
void show() {}
}
Если посмотреть его байткод, упросить его и мысленно декомпилировать обратно в Java, то получится:
public class Direction extends Enum {
public static final Direction LEFT = new Direction() {
@Override
void show() {
}
};
public static final Direction RIGTH = new Direction();
private static final Direction[] VALUES = values();
private Direction(String name, int ordinal) {
super(name, ordinal);
}
public static Direction[] values() {
return VALUES.clone();
}
public static Direction valueOf(String name) {
return Enum.valueOf(Direction.class, name);
}
void show() {
}
}
Т.е. enum превращается в класс со статическими public полями. И у класса создаются анонимные классы-наследники.
Собственно, если бы в Java не было enum - я бы реализовал перечисление примерно также
6) enum не может иметь public конструктор, и метод clone для него возвращает CloneNotSupportedException. И как я говорил выше он фактически финальный. Следовательно, во время работы программы число объектов enum равно число значений enum
7) из-за этого с помощью enum легко можно реализовать singleton. Можно, но не нужно, если вы используете Spring или другой IoC контейнер) Но об этом как-нибудь позже
8) исходя из сказанного выше enum-ы можно и нужно сравнивать по ==. Во-первых это нагляднее, а во-вторых меньше шансов получить NPE неправильно написав код сравнения.
9) хочу обратить внимание на классы EnumSet и EnumMap, которые эффективно с точки зрения расхода памяти позволяют использовать значения Enum в качестве ключей. Enum реализует Comparable, сравнение идет по полю ordinal - порядковому номеру значения.
10) Enum реализует Serializible и успешно сериализуется. Но есть одна важная особенность. При сериализации конкретного объекта сохраняется лишь его имя, и объект восстанавливается потом вызовом valueOf. Значения других полей теряются. Следовательно, enum должен быть реализован иммутабельным, значения всех полей должны задаваться в конструкторе.
#java
Всем привет! Есть такое правило - в логах в ПРОДе не должно быть ничего лишнего. Т.е. с одной стороны логи нужны для хранения стек-трейса исключения и другой полезной при разборе ошибки информации. Есть много инструментов, позволяющих строить по логам метрики, их парсить по заранее настроенной маске, вытаскивая полезную информацию. А с другой стороны логирование, как и любой другой функционал, жрет ресурсы. Как много? Вот так. На картинке показано число обработанных запросов для простейшего контроллера с логированием и без. Причём логирование происходило в RAM диск, т.е не учитываем задержки файловой системы. И шаблон сообщения в лог простой, контекст логирования для тех фреймворков, которые позволяют его настраивать, не используется. Вывод: отказываться от логирования не стоит, асинхрон рулит, а главное про уровни логирования и их настройку на ПРОДе забывать не стоит. Подробнее про условия теста: https://blog.sebastian-daschner.com/entries/logging-performance-comparison #java #logging #performance
Всем привет!
Сегодня поговорим про память в JVM.
Все знают про heap и stack. Начнем с них.
1) heap, она же куча. Хранит все создаваемые при работе JVM объекты. Очищается сборщиком мусора - garbage collector.
Делится на поколения: Eden, Survivor и Tenured (Old), между которыми перемещаются выжившие при сборке мусора объекты.
Кроме объектов начиная с Java 7 хранит также пул строк и другие пулы. Другие пулы - это пулы базовых типов-обверток над примитивами с фиксированным числом значений. Т.е. Boolean, Byte, Short, Integer, Long, Character. Цель использования пулов - уменьшение объема используемой памяти.
Раз пулы находятся в куче, то можно предположить, что неиспользуемые значения также удаляются сборщиком мусора. Так и есть.
Размер кучи можно регулировать параметрами: java -Xms256m -Xmx2048m
-Xms - начальный размер
-Xmx - максимальный размер.
По умолчанию максимальный размер для 64 разрядной системы при работе в сервером режиме равен 1/4 оперативной памяти, но не более 32 Гб.
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size
Посмотреть размер памяти у запущенного процесса: java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize.
Размер пулов тоже настраивается, пример для пула строк: -XX:StringTableSize=4901
Если памяти не хватило - вылетает OutOfMemoryError: Java heap space
2) stack. Хранит стек вызовов всех Java потоков. В стеке хранятся локальные переменные и параметры примитивных типов, а также ссылки на объекты локальных переменных и параметров. Сами объекты при этом хранятся в куче.
Размер регулируется параметром -Xss. Как правило занимает немного, хотя в теории 100 потоков - это 100 Мб. Не стоит забывать, что кроме созданных в коде потоков есть служебные - потоки сборщика мусора и потоки JIT компилятора. Если памяти не хватило, а это как правило означает бесконечную рекурсию, то вылетает https://stackoverflow.com/questions/214741/what-is-a-stackoverflowerror
Также некоторые могут вспомнить про MetaSpace)
3) MetaSpace, он же Class metadata. Хранит метаданные классов, загружаемые classloaders. А классов в Java много - стандартная библиотека rt.jar, Spring, другие используемые библиотеки, ваши собственные классы, генерация классов при работе программы.
До Java 8 классы хранились в области с названием PermGenSpace. Возможно кто-то еще помнит, а может и сталкивается до сих пор с OutOfMemoryError: PermGen space. (((
Отличия MetaSpace от PermGenSpace - неограниченный размер по умолчанию и то, что с ним наконец-то научился работать сборщик мусора. Хотя размер по умолчанию не ограничен, регулировать его при желании можно: XX:MaxMetaspaceSize и XX:CompressedClassSpaceMax. При желании можно получить и OutOfMemoryError: Metaspace)))
Оффтоп: почему опции две? В зависимости от включения UseCompressedOops и UseCompressedClassesPointers класс попадает либо в обычный MetaSpace, либо в MetaSpace для классов с сжатыми указателями. MaxMetaspaceSize включает в себя CompressedClassSpaceMax.
Про сжатые указатели https://habr.com/ru/post/440166/
Теперь пойдут области, покрытые мраком)
4) GC - область памяти, используемая Garbage Collector при работе. Занимает 3-4% от кучи. Также размер зависит от типа сборщика мусора.
5) JIT\Code - как известно, компилятор у Java довольно тупой, а вот JVM - умная) При работе она анализирует использование классов, выполняет разные оптимизации, а также компилирует часто используемые классы в нативный код. Процесс называется Just In Time Compilation. Именно в этой области скомпилированный код и хранится.
Размер регулируется ключом: XX:ReservedCodeCacheSize. Если установить слишком маленький размер - будет постоянная рекомпиляция, начнет страдать производительность. Больше опций, регулирующих процесс тут - https://docs.oracle.com/javase/8/embedded/develop-apps-platforms/codecache.htm
6) JIT\Compiler - собственно память, занимаемая компилятором.
Сегодня поговорим про память в JVM.
Все знают про heap и stack. Начнем с них.
1) heap, она же куча. Хранит все создаваемые при работе JVM объекты. Очищается сборщиком мусора - garbage collector.
Делится на поколения: Eden, Survivor и Tenured (Old), между которыми перемещаются выжившие при сборке мусора объекты.
Кроме объектов начиная с Java 7 хранит также пул строк и другие пулы. Другие пулы - это пулы базовых типов-обверток над примитивами с фиксированным числом значений. Т.е. Boolean, Byte, Short, Integer, Long, Character. Цель использования пулов - уменьшение объема используемой памяти.
Раз пулы находятся в куче, то можно предположить, что неиспользуемые значения также удаляются сборщиком мусора. Так и есть.
Размер кучи можно регулировать параметрами: java -Xms256m -Xmx2048m
-Xms - начальный размер
-Xmx - максимальный размер.
По умолчанию максимальный размер для 64 разрядной системы при работе в сервером режиме равен 1/4 оперативной памяти, но не более 32 Гб.
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size
Посмотреть размер памяти у запущенного процесса: java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize.
Размер пулов тоже настраивается, пример для пула строк: -XX:StringTableSize=4901
Если памяти не хватило - вылетает OutOfMemoryError: Java heap space
2) stack. Хранит стек вызовов всех Java потоков. В стеке хранятся локальные переменные и параметры примитивных типов, а также ссылки на объекты локальных переменных и параметров. Сами объекты при этом хранятся в куче.
Размер регулируется параметром -Xss. Как правило занимает немного, хотя в теории 100 потоков - это 100 Мб. Не стоит забывать, что кроме созданных в коде потоков есть служебные - потоки сборщика мусора и потоки JIT компилятора. Если памяти не хватило, а это как правило означает бесконечную рекурсию, то вылетает https://stackoverflow.com/questions/214741/what-is-a-stackoverflowerror
Также некоторые могут вспомнить про MetaSpace)
3) MetaSpace, он же Class metadata. Хранит метаданные классов, загружаемые classloaders. А классов в Java много - стандартная библиотека rt.jar, Spring, другие используемые библиотеки, ваши собственные классы, генерация классов при работе программы.
До Java 8 классы хранились в области с названием PermGenSpace. Возможно кто-то еще помнит, а может и сталкивается до сих пор с OutOfMemoryError: PermGen space. (((
Отличия MetaSpace от PermGenSpace - неограниченный размер по умолчанию и то, что с ним наконец-то научился работать сборщик мусора. Хотя размер по умолчанию не ограничен, регулировать его при желании можно: XX:MaxMetaspaceSize и XX:CompressedClassSpaceMax. При желании можно получить и OutOfMemoryError: Metaspace)))
Оффтоп: почему опции две? В зависимости от включения UseCompressedOops и UseCompressedClassesPointers класс попадает либо в обычный MetaSpace, либо в MetaSpace для классов с сжатыми указателями. MaxMetaspaceSize включает в себя CompressedClassSpaceMax.
Про сжатые указатели https://habr.com/ru/post/440166/
Теперь пойдут области, покрытые мраком)
4) GC - область памяти, используемая Garbage Collector при работе. Занимает 3-4% от кучи. Также размер зависит от типа сборщика мусора.
5) JIT\Code - как известно, компилятор у Java довольно тупой, а вот JVM - умная) При работе она анализирует использование классов, выполняет разные оптимизации, а также компилирует часто используемые классы в нативный код. Процесс называется Just In Time Compilation. Именно в этой области скомпилированный код и хранится.
Размер регулируется ключом: XX:ReservedCodeCacheSize. Если установить слишком маленький размер - будет постоянная рекомпиляция, начнет страдать производительность. Больше опций, регулирующих процесс тут - https://docs.oracle.com/javase/8/embedded/develop-apps-platforms/codecache.htm
6) JIT\Compiler - собственно память, занимаемая компилятором.
Stack Overflow
What is a StackOverflowError?
What is a StackOverflowError, what causes it, and how should I deal with them?
Ну и самые кишочки... )))
7) Internal - память под внутренние нужны JVM, не упомянутые выше.
В частности здесь выделяются Direct ByteBuffers - https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html В случае объекта в heap, содержащего данные файла, ОС вначале копирует данные в свой низкоуровневый буфер, а потом JVM копирует данные к себе, то в случае Direct ByteBuffers этого можно избежать. Используются во многих высоконагруженных системах, в частности Kafka.
Размер настраивается через XX:MaxDirectMemorySize=N. Нехватку памяти можно определить по "OutOfMemoryError: Direct buffer memory".
Т.к. ByteBuffer - объект, то сборщик мусора также умеет убирать и данные, выделенные в Direct ByteBuffers. Правда с некоторой задержкой, т.к. сами данные все же находятся не в куче, и механизм уборки получается чуть более сложный.
Сравнение скорости работы прямого и heap буфера с комментарием о том, что это всего лишь один из возможных случаев использования: https://elizarov.livejournal.com/20381.html
А увидеть все эти области можно запустив java процесс со специальным ключом XX:NativeMemoryTracking и используя утилиту jcmd из состава JDK.
Cм. детали тут https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html
Запускать в ПРОДе с этой опцией нужно с осторожностью, overhead может быть 5-10%
Может показаться, что главное - это heap, все остальное по сравнению с ним - мелочи. Это не всегда так, см. исследование https://shipilev.net/jvm/anatomy-quarks/
#java #interview_question
7) Internal - память под внутренние нужны JVM, не упомянутые выше.
В частности здесь выделяются Direct ByteBuffers - https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html В случае объекта в heap, содержащего данные файла, ОС вначале копирует данные в свой низкоуровневый буфер, а потом JVM копирует данные к себе, то в случае Direct ByteBuffers этого можно избежать. Используются во многих высоконагруженных системах, в частности Kafka.
Размер настраивается через XX:MaxDirectMemorySize=N. Нехватку памяти можно определить по "OutOfMemoryError: Direct buffer memory".
Т.к. ByteBuffer - объект, то сборщик мусора также умеет убирать и данные, выделенные в Direct ByteBuffers. Правда с некоторой задержкой, т.к. сами данные все же находятся не в куче, и механизм уборки получается чуть более сложный.
Сравнение скорости работы прямого и heap буфера с комментарием о том, что это всего лишь один из возможных случаев использования: https://elizarov.livejournal.com/20381.html
А увидеть все эти области можно запустив java процесс со специальным ключом XX:NativeMemoryTracking и используя утилиту jcmd из состава JDK.
Cм. детали тут https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html
Запускать в ПРОДе с этой опцией нужно с осторожностью, overhead может быть 5-10%
Может показаться, что главное - это heap, все остальное по сравнению с ним - мелочи. Это не всегда так, см. исследование https://shipilev.net/jvm/anatomy-quarks/
#java #interview_question
Livejournal
Детали реализации имеют значение или direct ByteBuffer vs heap ByteBuffer
При написании кода для максимальной производительности особое внимание необходимо уделять используемым библиотекам, даже если эти библиотеки являются частью вашего языка программирования. Необходимо подробно изучать не только контракты, описанные в их API…
Для иллюстрации предыдущего поста. Вот как разместиться в памяти такой код:
class MainClass {
void method1() { //<- main
int variable1 = 1;
Class1 variable2 = new Class1();
variable2.method2();
}
}
class Class1 {
static Class2 classVariable4 = new Class2();
int instanceVariable5 = 0;
Class2 instanceVariable6 = new Class2();
void method2() {
int variable3 = 3;
}
}
class Class2 { }
Взял отсюда https://stackoverflow.com/questions/362740/java-memory-model-can-someone-explain-it/362804
class MainClass {
void method1() { //<- main
int variable1 = 1;
Class1 variable2 = new Class1();
variable2.method2();
}
}
class Class1 {
static Class2 classVariable4 = new Class2();
int instanceVariable5 = 0;
Class2 instanceVariable6 = new Class2();
void method2() {
int variable3 = 3;
}
}
class Class2 { }
Взял отсюда https://stackoverflow.com/questions/362740/java-memory-model-can-someone-explain-it/362804
Всем привет!
Что входит в состав JDK - Java Development Kit - кроме стандартной библиотеки Java?
Разные утилиты, сразу можно вспомнить java и javaс. Но не только они, есть еще ряд полезных консольных утилит, о которых расскажу ниже.
Сразу скажу, про таких монстров, как JVisualVM и Mission Control, стоит рассказать отдельно, что я и сделаю. Но потом)
Все утилиты лежат в папке bin внутри JDK.
Поехали:
1) javap - прочитать версию байткода, а также увидеть байткод в человекочитаемом виде.
Как запускать: javap -verbose Class1.class
2) jcmd - позволяет отправлять команды работающему процессу. Что за команды - да самые разные, например:
- сделать дамп кучи
- показать статистику использования памяти, причем после некоторых манипуляция - любых областей памяти, не только кучи
- время работы и другую информацию о JVM
- информацию о запущенных процессах
Если запусить jcmd без параметров - покажет список работающих java процессов.
Если запустить ее как jcmd PID - покажет доступные для процесса команды.
Описание почитать можно тут https://docs.oracle.com/en/java/javase/17/troubleshoot/diagnostic-tools.html#GUID-42A18B29-B4AD-4831-B846-2CDBA55F2254
3) jstat - показывает краткую статистику по Java процессу.
jstat -class PID - иннформация по загрузке классов
jstat -compiler PID - по JIT компиляции
jstat -gc PID - информация по работе GC, ее расшифровку и другие опции см. тут https://docs.oracle.com/en/java/javase/11/tools/jstat.html#GUID-5F72A7F9-5D5A-4486-8201-E1D1BA8ACCB5
Аналог линуксовых vmstat, iostat.
4) keytool - утилита для работы с сертификатами в формате jks.
5) jshell - начиная с Java 9 появляется консоль для выполнения Java кода интерактивно, без создания классов.
Что удобно: не нужно создавать класс, import-ы, ставить ; в конце строки, просто можно проверить выполнение какого-то простого кода.
6) jdeprscan - сканирует jar, класс или папку на наличие Deprecated API. Можно использовать в DevOps.
7) jconsole - графическая утилита для отображения использования CPU, памяти и другой информации, которую может отображать jcmd. Также показывает MBean-ы. Главное ее достоинство заключается в том, что она полностью бесплатна и есть в любой JDK.
При этом JVisualVM и Mission Control могут гораздо больше, но с ними есть нюансы)
Полный список утилит можно найти здесь: https://docs.oracle.com/en/java/javase/11/tools/tools-and-command-reference.html
#jdk #java #tools
Что входит в состав JDK - Java Development Kit - кроме стандартной библиотеки Java?
Разные утилиты, сразу можно вспомнить java и javaс. Но не только они, есть еще ряд полезных консольных утилит, о которых расскажу ниже.
Сразу скажу, про таких монстров, как JVisualVM и Mission Control, стоит рассказать отдельно, что я и сделаю. Но потом)
Все утилиты лежат в папке bin внутри JDK.
Поехали:
1) javap - прочитать версию байткода, а также увидеть байткод в человекочитаемом виде.
Как запускать: javap -verbose Class1.class
2) jcmd - позволяет отправлять команды работающему процессу. Что за команды - да самые разные, например:
- сделать дамп кучи
- показать статистику использования памяти, причем после некоторых манипуляция - любых областей памяти, не только кучи
- время работы и другую информацию о JVM
- информацию о запущенных процессах
Если запусить jcmd без параметров - покажет список работающих java процессов.
Если запустить ее как jcmd PID - покажет доступные для процесса команды.
Описание почитать можно тут https://docs.oracle.com/en/java/javase/17/troubleshoot/diagnostic-tools.html#GUID-42A18B29-B4AD-4831-B846-2CDBA55F2254
3) jstat - показывает краткую статистику по Java процессу.
jstat -class PID - иннформация по загрузке классов
jstat -compiler PID - по JIT компиляции
jstat -gc PID - информация по работе GC, ее расшифровку и другие опции см. тут https://docs.oracle.com/en/java/javase/11/tools/jstat.html#GUID-5F72A7F9-5D5A-4486-8201-E1D1BA8ACCB5
Аналог линуксовых vmstat, iostat.
4) keytool - утилита для работы с сертификатами в формате jks.
5) jshell - начиная с Java 9 появляется консоль для выполнения Java кода интерактивно, без создания классов.
Что удобно: не нужно создавать класс, import-ы, ставить ; в конце строки, просто можно проверить выполнение какого-то простого кода.
6) jdeprscan - сканирует jar, класс или папку на наличие Deprecated API. Можно использовать в DevOps.
7) jconsole - графическая утилита для отображения использования CPU, памяти и другой информации, которую может отображать jcmd. Также показывает MBean-ы. Главное ее достоинство заключается в том, что она полностью бесплатна и есть в любой JDK.
При этом JVisualVM и Mission Control могут гораздо больше, но с ними есть нюансы)
Полный список утилит можно найти здесь: https://docs.oracle.com/en/java/javase/11/tools/tools-and-command-reference.html
#jdk #java #tools
Oracle Help Center
Troubleshooting Guide
The Java Development Kit (JDK) provides diagnostic tools and troubleshooting tools specific to various operating systems. Custom diagnostic tools can also be developed using the APIs provided by the JDK.
Всем привет!
Я вернулся из отпуска, значит настало время для нового поста.
Сегодня хотел бы рассказать о об одном своем провальном проекте. Ведь далеко не все проекты попадают в ПРОМ.
И это нормально, главное - делать выводы из ошибок.
Поехали.
Задача стояла такая. Есть две БД с одинаковой схемой - структурой таблиц.
Данные в БД - пользователь + связанная с ним информация. А это еще порядка сотни таблиц, причем их число растет с каждым релизом.
Требуется периодически переносить часть пользователей из одной БД в другую. Назовем утилиту, которая будет это делать - мигратор.
Навскидку видится три варианта:
1) ETL
2) самописный скрипт БД
3) Java мигратор, работающий на основании метаданных из Hibernate. Да, забыл уточнить, есть Java приложение, работающее со всеми таблицами пользователя через Hibernate.
Наша команда занималась третьим вариантом. Какие я сейчас вижу проблемы у этого варианта:
1) Java мигратор самый сложный и непрозрачный из всех вариантов. Главный его плюс - он практически убирает необходимость ручной доработки мигратора с выходом новых релизов. И убирает двойную работу - другие варианты требуют сначала обновления скриптов версионирования БД, а потом обновления мигратора. Где может выстрелить сложность Java мигратора? БД сопровождают специально обученные люди - DBA. Они достаточно консервативны, т.к. во-первых они сопровождение, а во-вторых - DBA) В нашем случае на ПРОМ скрипты накатывались вручную, хотя разработка предоставляла Liquibase скрипты. Изначально со стороны DBA было заметно недоверие к Java мигратору. Чтобы его снизить решили, что мигратор будет реконструировать схему БД, создавать список таблиц, связанных с пользователем, а DBA всегда будут иметь возможность отфильтровать этот список. Т.е. миграция данных идет по "белому списку". Сопровождение предварительно одобрило такой. При этом активно в процессе выставления требований и тестирования не участвовало.
2) отсутствие внятной процедуры приемки скриптов. Снизить непрозрачность мигратора можно с помощью создания набора тест-кейсов со 100% покрытием на ПРОМ like обезличенных данных. С согласованием процедуры у DBA. Мы этого не сделали. Т.е. тестирование конечно было, но оно проводилось на одном тестовом клиенте, нормального плана не было, консультации с DBA были фрагментарными.
3) растянутость разработки во времени из-за недостатка времени. Задача была экспериментальной, разработка шла больше года, с переодическим переключением на другие задачи. Это привело к падению качества кода. Ни я как тимлид, ни разработчики не дошли до нормального рефакторинга. Модульных тестов было мало. Плюс использовалась рефлексия, что еще больше усложнило код. Когда к моменту принятия решения о внедрении или не внедрении Java мигратора когда я смотрел на код - мне было страшновато(((
4) задача не была вовремя закрыта. На задачу спалили больше человекогода. В результате на ПРОМ использовался самописный SQL мигратор. Сейчас я бы попробовал получить от заказчика четкое ОК на внедрение, создав совместно с ним план тестирования и приемки. Agile и все такое) А если бы этого ОК не было - прекратил бы разработку.
#projects #JPA #fuckup
Я вернулся из отпуска, значит настало время для нового поста.
Сегодня хотел бы рассказать о об одном своем провальном проекте. Ведь далеко не все проекты попадают в ПРОМ.
И это нормально, главное - делать выводы из ошибок.
Поехали.
Задача стояла такая. Есть две БД с одинаковой схемой - структурой таблиц.
Данные в БД - пользователь + связанная с ним информация. А это еще порядка сотни таблиц, причем их число растет с каждым релизом.
Требуется периодически переносить часть пользователей из одной БД в другую. Назовем утилиту, которая будет это делать - мигратор.
Навскидку видится три варианта:
1) ETL
2) самописный скрипт БД
3) Java мигратор, работающий на основании метаданных из Hibernate. Да, забыл уточнить, есть Java приложение, работающее со всеми таблицами пользователя через Hibernate.
Наша команда занималась третьим вариантом. Какие я сейчас вижу проблемы у этого варианта:
1) Java мигратор самый сложный и непрозрачный из всех вариантов. Главный его плюс - он практически убирает необходимость ручной доработки мигратора с выходом новых релизов. И убирает двойную работу - другие варианты требуют сначала обновления скриптов версионирования БД, а потом обновления мигратора. Где может выстрелить сложность Java мигратора? БД сопровождают специально обученные люди - DBA. Они достаточно консервативны, т.к. во-первых они сопровождение, а во-вторых - DBA) В нашем случае на ПРОМ скрипты накатывались вручную, хотя разработка предоставляла Liquibase скрипты. Изначально со стороны DBA было заметно недоверие к Java мигратору. Чтобы его снизить решили, что мигратор будет реконструировать схему БД, создавать список таблиц, связанных с пользователем, а DBA всегда будут иметь возможность отфильтровать этот список. Т.е. миграция данных идет по "белому списку". Сопровождение предварительно одобрило такой. При этом активно в процессе выставления требований и тестирования не участвовало.
2) отсутствие внятной процедуры приемки скриптов. Снизить непрозрачность мигратора можно с помощью создания набора тест-кейсов со 100% покрытием на ПРОМ like обезличенных данных. С согласованием процедуры у DBA. Мы этого не сделали. Т.е. тестирование конечно было, но оно проводилось на одном тестовом клиенте, нормального плана не было, консультации с DBA были фрагментарными.
3) растянутость разработки во времени из-за недостатка времени. Задача была экспериментальной, разработка шла больше года, с переодическим переключением на другие задачи. Это привело к падению качества кода. Ни я как тимлид, ни разработчики не дошли до нормального рефакторинга. Модульных тестов было мало. Плюс использовалась рефлексия, что еще больше усложнило код. Когда к моменту принятия решения о внедрении или не внедрении Java мигратора когда я смотрел на код - мне было страшновато(((
4) задача не была вовремя закрыта. На задачу спалили больше человекогода. В результате на ПРОМ использовался самописный SQL мигратор. Сейчас я бы попробовал получить от заказчика четкое ОК на внедрение, создав совместно с ним план тестирования и приемки. Agile и все такое) А если бы этого ОК не было - прекратил бы разработку.
#projects #JPA #fuckup
Всем привет!
Я довольно часто провожу собесы Java разработчиков. Хотел бы написать про свои "черные метки", сильно снижающие шанс получить предложение о работе.
Ясно, что оценка будет комплексная - по теории, практике, софт-скилам, соответствию знаний и запрашиваемого "золота", но перечисленные ниже вещи сильно влияют на результат.
Поехали.
1) нет понимания основ Java, которые я ожидаю даже от джуна. Пример: переменную нельзя инициализировать результатом метода, обязательно должна быть инициализация через конструктор, а уже потом вызов метода с присвоением.
Для сравнения - не относится к основам незнание, когда можно не инициализировать переменную, а когда нет. Это приходит с опытом.
Еще примеры: непонимание иммутабельности строк или разницы между == и equals. Подчеркну - именно незнание. Можно не заметить этот баг в коде, такое к сожалению бывает, лечится подсказками IDEA и опытом
2) непонимание или отсутствие опыта работы со Spring Core, он же Spring Ioc. Spring сейчас - это наше все. Говорим Java приложение, подразумеваем Spring) Можно его не использовать: знаю компанию, где это запрещено по причине того, что Spring под капотом сложный и многие его используют не разбираясь в деталях. Это допустимый, хотя и спорный подход. Но он не отменяет того, что основы Spring нужно знать.
3) незнание модульных тестов. На мой взгляд это базовая практика, в отличие от более сложных вещей типа TDD, DDD, паттернов и ее нужно изучать вместе с Java и Spring
4) отсутсвие навыков работы с чужим кодом. На собес я предлагаю задачку типа код-ревью. Хотя бы 25% заложенных туда багов надо найти) Отдельный момент - есть баги логические, есть неоптимальный код, есть плохой код-стайл. Первые выглядят как более важные, но как правило их находят при тестировании - модульном или функциональном. Естественно, избавившись от них раньше, мы уменьшим время тестирования, Shift Left, Lead Time и все такое) Но вторые и третьи тоже важны, потому что практически любой код не является одноразовым. Его будут править - исправлять баги, добавлять новые фичи, рефакторить. Возможно даже другие люди. И разбираться в неоптимальном коде, где много boiler plate, отсутствует code стайл - то еще удовольствие. И куча потраченного времени, желание выкинуть все нафиг и переписать с нуля, а это еще куча потраченного времени...
5) Возвращаясь к задачке с код-ревью на собесе. В ней есть работа с данными, содержащимися в локальных переменных. Данные не передаются в метод через параметры - создаются в методе или читаются из файла. Но есть соискатели, которые начинают беспокоится за сохранность этих данных при многоточном вызове метода. Предлагают использовать StringBuffer для манипуляция со строками, или отказаться от static метода и хранить все в полях. Незачет) Локальные объекты хранятся в куче вместе со всеми, но ссылка на них есть только у одного потока, беспокоится незачем. Вообще это основы Java, но вынес в отдельный пункт, т.к. прямо бесит)
6) Для Middle + обязательно знание принципов SOLID. Можно не знать все паттерны или скептически относится к их практическому применению. Тут можно обсудить, поспорить. Но SOLID - это как аксиомы в математике. Основа разработки. Для джуна тоже будет огромным плюсом.
7) односложные ответы без попыток рассуждения. Все знать нельзя, да и не нужно, но рассуждая ты показываешь, на что способен. Сюда же я бы отнес проблемы с логикой. Например: "я первый раз вижу конструкцию do .. while, значит код работать не будет". Чтобы утверждать, что код не работает, нужно хорошо знать Java. Подход - я этого не знаю, поэтому оно работать не будет - плохой))) Везде, не только в Java. Правильно: я этого не знаю, поэтому я бы использовал другую конструкцию.
8) ну и очевидное - уход от прямых ответов, разливание воды ведрами, явная ложь.
#java #interview #solid
Я довольно часто провожу собесы Java разработчиков. Хотел бы написать про свои "черные метки", сильно снижающие шанс получить предложение о работе.
Ясно, что оценка будет комплексная - по теории, практике, софт-скилам, соответствию знаний и запрашиваемого "золота", но перечисленные ниже вещи сильно влияют на результат.
Поехали.
1) нет понимания основ Java, которые я ожидаю даже от джуна. Пример: переменную нельзя инициализировать результатом метода, обязательно должна быть инициализация через конструктор, а уже потом вызов метода с присвоением.
Для сравнения - не относится к основам незнание, когда можно не инициализировать переменную, а когда нет. Это приходит с опытом.
Еще примеры: непонимание иммутабельности строк или разницы между == и equals. Подчеркну - именно незнание. Можно не заметить этот баг в коде, такое к сожалению бывает, лечится подсказками IDEA и опытом
2) непонимание или отсутствие опыта работы со Spring Core, он же Spring Ioc. Spring сейчас - это наше все. Говорим Java приложение, подразумеваем Spring) Можно его не использовать: знаю компанию, где это запрещено по причине того, что Spring под капотом сложный и многие его используют не разбираясь в деталях. Это допустимый, хотя и спорный подход. Но он не отменяет того, что основы Spring нужно знать.
3) незнание модульных тестов. На мой взгляд это базовая практика, в отличие от более сложных вещей типа TDD, DDD, паттернов и ее нужно изучать вместе с Java и Spring
4) отсутсвие навыков работы с чужим кодом. На собес я предлагаю задачку типа код-ревью. Хотя бы 25% заложенных туда багов надо найти) Отдельный момент - есть баги логические, есть неоптимальный код, есть плохой код-стайл. Первые выглядят как более важные, но как правило их находят при тестировании - модульном или функциональном. Естественно, избавившись от них раньше, мы уменьшим время тестирования, Shift Left, Lead Time и все такое) Но вторые и третьи тоже важны, потому что практически любой код не является одноразовым. Его будут править - исправлять баги, добавлять новые фичи, рефакторить. Возможно даже другие люди. И разбираться в неоптимальном коде, где много boiler plate, отсутствует code стайл - то еще удовольствие. И куча потраченного времени, желание выкинуть все нафиг и переписать с нуля, а это еще куча потраченного времени...
5) Возвращаясь к задачке с код-ревью на собесе. В ней есть работа с данными, содержащимися в локальных переменных. Данные не передаются в метод через параметры - создаются в методе или читаются из файла. Но есть соискатели, которые начинают беспокоится за сохранность этих данных при многоточном вызове метода. Предлагают использовать StringBuffer для манипуляция со строками, или отказаться от static метода и хранить все в полях. Незачет) Локальные объекты хранятся в куче вместе со всеми, но ссылка на них есть только у одного потока, беспокоится незачем. Вообще это основы Java, но вынес в отдельный пункт, т.к. прямо бесит)
6) Для Middle + обязательно знание принципов SOLID. Можно не знать все паттерны или скептически относится к их практическому применению. Тут можно обсудить, поспорить. Но SOLID - это как аксиомы в математике. Основа разработки. Для джуна тоже будет огромным плюсом.
7) односложные ответы без попыток рассуждения. Все знать нельзя, да и не нужно, но рассуждая ты показываешь, на что способен. Сюда же я бы отнес проблемы с логикой. Например: "я первый раз вижу конструкцию do .. while, значит код работать не будет". Чтобы утверждать, что код не работает, нужно хорошо знать Java. Подход - я этого не знаю, поэтому оно работать не будет - плохой))) Везде, не только в Java. Правильно: я этого не знаю, поэтому я бы использовал другую конструкцию.
8) ну и очевидное - уход от прямых ответов, разливание воды ведрами, явная ложь.
#java #interview #solid
Всем привет!
Сегодня хочу рассказать про один интересный алгоритм - фильтр Блума.
Для начала ссылка на вики или хабр - по вкусу)
https://ru.wikipedia.org/wiki/Фильтр_Блума
https://habr.com/ru/company/otus/blog/541378/
Ссылки сразу можно не читать, там много теории, расскажу в чем суть на примере.
Есть два микросервиса. Первый предварительно обрабатывает запросы клиента, назовем его web controller. Второй - оркестратор, выполняет длительные операции. web controller выполняет авторизацию, валидацию, кэширование...
Предположим, мы разрешаем выполнение некой операции по черному списку. Или белому - не суть. Это может быть список клиентов, телефонов, диапазонов IP. Список большой, миллионы элементов. Выглядит логично использовать controller для фильтрации, чтобы снять лишнюю нагрузку со оркестратора. Но список динамический, он хранится в отдельном микросервисе.
Какие есть варианты:
1) вызывать микросервис черного списка. Получаем задержку обработки клиентского запроса
2) закэшировать данные в controller с неким TTL. Уже лучше, но так мы потеряем сотни Мб ОЗУ. А это лишь часть функционала controller.
3) закэшировать хэши значений списка. Размер данных скорее всего уменьшится, зависит от выбора хэш-функции и размера данных. Но все равно - десятки Мб.
4) использовать фильтр Блума. Вот калькулятор, показывающий сколько займут данные в фильтре Блума - https://hur.st/bloomfilter/?n=1000000&p=0.01&m=&k=. Как правило, объем меньше раз в 10 благодаря хитрой математике.
Но чудес не бывает, есть подводный камень - фильтр допускает ложноположительные ответы. Т.е. ответ false на запрос входит ли значение в список - это всегда false, а true - с некой вероятностью не true. Вероятность - входной параметр фильтра.
Чем меньше % ошибок - тем больше объем данных.
Соответственно, в нашем примере проверка по черному списку должна быть и в оркестраторе. Но при этом 99.99% "левых" запросов будет отбито в controller.
Может возникнуть вопрос: дублирование - это же плохо? Увы, в микросервисной архитектуре дублирование проверок - это нормально. Главное, не допускать дублирования реализации алгоритма проверки, иначе при его изменении возможен рассинхрон и неприятные баги.
Реализовывать алгоритм самому не нужно, все-таки под капотом у него довольно сложная математика. Есть готовая реализация в Google Guava: https://www.baeldung.com/guava-bloom-filter
#algorithm #java #cache
Сегодня хочу рассказать про один интересный алгоритм - фильтр Блума.
Для начала ссылка на вики или хабр - по вкусу)
https://ru.wikipedia.org/wiki/Фильтр_Блума
https://habr.com/ru/company/otus/blog/541378/
Ссылки сразу можно не читать, там много теории, расскажу в чем суть на примере.
Есть два микросервиса. Первый предварительно обрабатывает запросы клиента, назовем его web controller. Второй - оркестратор, выполняет длительные операции. web controller выполняет авторизацию, валидацию, кэширование...
Предположим, мы разрешаем выполнение некой операции по черному списку. Или белому - не суть. Это может быть список клиентов, телефонов, диапазонов IP. Список большой, миллионы элементов. Выглядит логично использовать controller для фильтрации, чтобы снять лишнюю нагрузку со оркестратора. Но список динамический, он хранится в отдельном микросервисе.
Какие есть варианты:
1) вызывать микросервис черного списка. Получаем задержку обработки клиентского запроса
2) закэшировать данные в controller с неким TTL. Уже лучше, но так мы потеряем сотни Мб ОЗУ. А это лишь часть функционала controller.
3) закэшировать хэши значений списка. Размер данных скорее всего уменьшится, зависит от выбора хэш-функции и размера данных. Но все равно - десятки Мб.
4) использовать фильтр Блума. Вот калькулятор, показывающий сколько займут данные в фильтре Блума - https://hur.st/bloomfilter/?n=1000000&p=0.01&m=&k=. Как правило, объем меньше раз в 10 благодаря хитрой математике.
Но чудес не бывает, есть подводный камень - фильтр допускает ложноположительные ответы. Т.е. ответ false на запрос входит ли значение в список - это всегда false, а true - с некой вероятностью не true. Вероятность - входной параметр фильтра.
Чем меньше % ошибок - тем больше объем данных.
Соответственно, в нашем примере проверка по черному списку должна быть и в оркестраторе. Но при этом 99.99% "левых" запросов будет отбито в controller.
Может возникнуть вопрос: дублирование - это же плохо? Увы, в микросервисной архитектуре дублирование проверок - это нормально. Главное, не допускать дублирования реализации алгоритма проверки, иначе при его изменении возможен рассинхрон и неприятные баги.
Реализовывать алгоритм самому не нужно, все-таки под капотом у него довольно сложная математика. Есть готовая реализация в Google Guava: https://www.baeldung.com/guava-bloom-filter
#algorithm #java #cache
Wikipedia
Фильтр Блума
Фи́льтр Блу́ма (англ. Bloom filter) — вероятностная структура данных, сконструированная Бёртоном Блумом в 1970 году, позволяющая проверять принадлежность элемента к множеству. При этом существует возможность получить ложноположительное срабатывание (элемента…
Всем привет!
Современная разработка - это микросервисы, микросервисы - это много внешних взаимодействий, а внешние взаимодействия - это API.
Каким должно быть хорошее API?
1) API должно быть версионировано. Все течет, все меняется: бизнес-требования, новые типы потребителей. О версионировании нужно подумать заранее.
2) Сервер и клиенты поддерживают работу с несколькими версиями API. Это позволит избежать синхронных внедрений двух и более микросервисов. Синхронное внедрение - это сложно и повышает вероятность нарушить все наши "девятки" (SLA)
3) API описано схемой. Король горы для REST запросов - OpenAPI. Отмечу, что в отличие от JSON Schema или XSD OpenAPI описывает не модель данных, а web сервис целиком: поддерживаемые HTTP методы - GET, POST,..., коды ответов HTTP, параметры запроса, заголовки.
4) CDC тестирование - https://martinfowler.com/articles/consumerDrivenContracts.html#Consumer-drivenContracts
В чем суть: создается контракт - человеко и машиночитаемое описание тест-кейсов для API, на основании которых генерируются API тесты для поставщика и потребителя. Примеры реализаций:
Spring Cloud Contract https://docs.spring.io/spring-cloud-contract/docs/current/reference/html/getting-started.html#getting-started-three-second-tour
Pact https://docs.pact.io/5-minute-getting-started-guide
Надо отметить, что OpenAPI и CDS в ряде моментов дублируют информацию, об этой проблеме и возможном решении для Spring можно почитать тут: https://springframework.guru/defining-spring-cloud-contracts-in-open-api/
5) API должно предусматривать уникальный идентификатор запроса, обеспечивающий идемпотентность - возможность повторного вызова метода API, не приводящего к дублированию данных на сервере. В сети и на сервере случаются сбои, повтор вызова в этом случае - естественный выход из ситуации. При получении второго и последующего запросов сервер, определив по уникальному идентификатору, что он уже получал этот запрос, должен вернуть текущий статус операции
6) API должно содержать trace_id - уникальный идентификатор операции. trace_id генерируется на клиенте и копируется в каждый последующий запрос по всем микросервисам. Потом с помощью таких инструментов, как Jaeger дерево запросов визуализируется, что упрощает отлавливание ошибок
7) в API не должно быть ничего лишнего. Добавляем то, что нужно клиенту, если появляется новые требования - версионируем API. Иначе получим аналог "божественного объекта" - https://ru.wikipedia.org/wiki/Божественный_объект
#API #arch #tracing
Современная разработка - это микросервисы, микросервисы - это много внешних взаимодействий, а внешние взаимодействия - это API.
Каким должно быть хорошее API?
1) API должно быть версионировано. Все течет, все меняется: бизнес-требования, новые типы потребителей. О версионировании нужно подумать заранее.
2) Сервер и клиенты поддерживают работу с несколькими версиями API. Это позволит избежать синхронных внедрений двух и более микросервисов. Синхронное внедрение - это сложно и повышает вероятность нарушить все наши "девятки" (SLA)
3) API описано схемой. Король горы для REST запросов - OpenAPI. Отмечу, что в отличие от JSON Schema или XSD OpenAPI описывает не модель данных, а web сервис целиком: поддерживаемые HTTP методы - GET, POST,..., коды ответов HTTP, параметры запроса, заголовки.
4) CDC тестирование - https://martinfowler.com/articles/consumerDrivenContracts.html#Consumer-drivenContracts
В чем суть: создается контракт - человеко и машиночитаемое описание тест-кейсов для API, на основании которых генерируются API тесты для поставщика и потребителя. Примеры реализаций:
Spring Cloud Contract https://docs.spring.io/spring-cloud-contract/docs/current/reference/html/getting-started.html#getting-started-three-second-tour
Pact https://docs.pact.io/5-minute-getting-started-guide
Надо отметить, что OpenAPI и CDS в ряде моментов дублируют информацию, об этой проблеме и возможном решении для Spring можно почитать тут: https://springframework.guru/defining-spring-cloud-contracts-in-open-api/
5) API должно предусматривать уникальный идентификатор запроса, обеспечивающий идемпотентность - возможность повторного вызова метода API, не приводящего к дублированию данных на сервере. В сети и на сервере случаются сбои, повтор вызова в этом случае - естественный выход из ситуации. При получении второго и последующего запросов сервер, определив по уникальному идентификатору, что он уже получал этот запрос, должен вернуть текущий статус операции
6) API должно содержать trace_id - уникальный идентификатор операции. trace_id генерируется на клиенте и копируется в каждый последующий запрос по всем микросервисам. Потом с помощью таких инструментов, как Jaeger дерево запросов визуализируется, что упрощает отлавливание ошибок
7) в API не должно быть ничего лишнего. Добавляем то, что нужно клиенту, если появляется новые требования - версионируем API. Иначе получим аналог "божественного объекта" - https://ru.wikipedia.org/wiki/Божественный_объект
#API #arch #tracing
martinfowler.com
Consumer-Driven Contracts: A Service Evolution
Pattern
Pattern
Consumers should drive the definition of service contracts, while limiting to their individual needs. Suppliers should then validate against the union of their consumers' contracts.
Всем привет!
Что должен знать разработчик кроме своего основного языка, в нашем случае Java. Или Kotlin. Паттерны, основы архитектуры, фреймворки - это я включаю в знание Java.
1) git. На самом деле систем хранения исходников много - svn, mercurial, darcs, git, но последний победил всех. Да, многое можно сделать в IDEA или графических клиентах типа TortoiseGit. Но часто возникают задачи автоматизации набора повторящих команд или разные нетривиальные вещи типа смены автора коммита по всей истории. Кроме базовых команд надо знать основные модели ветвления - gitflow, githab flow, gitlab flow, trunk-based и условия их применимости
2) SQL. Почему - большинство сервисов имеет хранилище, очень часто оно реляционное. Даже если noSQL - к нему может быть прикручен слой SQL-подобного API. Даже если ORM - там есть JQL или HQL.
Объем знаний: DML, DDL, PK, FK, sequences, индексы, партиционирование, неплохо понимать план выполнения запроса
3) shell scripts. Частно приходится выполнять какие-то повторяющиеся действия на рабочей машине или на сервере. Сервера на 90% это Linux, рабочие машины по разному, но для Windows есть несколько реализаций Linux shell. На N-ом повторе возникает мысль про автоматизацию. Часто готовые скрипты можно найти на просторах сети, но в любом случае нужно понимать, что они делают. Чтобы случайно не отформатировать диск C)))
Набор команд, примерный: cd, cp, grep, cat, tail, less, echo, ps, top, tar, gz, sudo, chmod, wget, curl, apt-get или его аналог. Сюда же уметь сгенерировать ssh ключи и сертификаты. Также неплохо бы знать vi. Уточнение - учить наизусть все ключи не нужно, важно понимать что можно сделать в shell, принцип pipeline и какие команды что делают.
4) RegExp. Потребность возникает как при кодирование, так и при отладке - например, для поиска по логам. Опять же есть готовые выражения, есть крутой, хотя небесплатный редактор RegExp Buddy, но читать этот язык надо уметь
5) build tool. На данный момент это Maven и\или Gradle. Опять же IDEA умеет компилировать проекты сама, но на любом более менее сложном нужна утилита для сборки.
Объем: build lifecycle, управление зависимостями, многомодульные проекты, управление настройками сборки, плагины и их прикручивание к нужному шагу сборки
6) docker. Мы идем в светлое облачное будущее, Docker - это его основа.
Что важно: зачем нужен Docker, Docker Registry, слоистость образов, структура Docker файла
to be continued
#java #tools #обучение
Что должен знать разработчик кроме своего основного языка, в нашем случае Java. Или Kotlin. Паттерны, основы архитектуры, фреймворки - это я включаю в знание Java.
1) git. На самом деле систем хранения исходников много - svn, mercurial, darcs, git, но последний победил всех. Да, многое можно сделать в IDEA или графических клиентах типа TortoiseGit. Но часто возникают задачи автоматизации набора повторящих команд или разные нетривиальные вещи типа смены автора коммита по всей истории. Кроме базовых команд надо знать основные модели ветвления - gitflow, githab flow, gitlab flow, trunk-based и условия их применимости
2) SQL. Почему - большинство сервисов имеет хранилище, очень часто оно реляционное. Даже если noSQL - к нему может быть прикручен слой SQL-подобного API. Даже если ORM - там есть JQL или HQL.
Объем знаний: DML, DDL, PK, FK, sequences, индексы, партиционирование, неплохо понимать план выполнения запроса
3) shell scripts. Частно приходится выполнять какие-то повторяющиеся действия на рабочей машине или на сервере. Сервера на 90% это Linux, рабочие машины по разному, но для Windows есть несколько реализаций Linux shell. На N-ом повторе возникает мысль про автоматизацию. Часто готовые скрипты можно найти на просторах сети, но в любом случае нужно понимать, что они делают. Чтобы случайно не отформатировать диск C)))
Набор команд, примерный: cd, cp, grep, cat, tail, less, echo, ps, top, tar, gz, sudo, chmod, wget, curl, apt-get или его аналог. Сюда же уметь сгенерировать ssh ключи и сертификаты. Также неплохо бы знать vi. Уточнение - учить наизусть все ключи не нужно, важно понимать что можно сделать в shell, принцип pipeline и какие команды что делают.
4) RegExp. Потребность возникает как при кодирование, так и при отладке - например, для поиска по логам. Опять же есть готовые выражения, есть крутой, хотя небесплатный редактор RegExp Buddy, но читать этот язык надо уметь
5) build tool. На данный момент это Maven и\или Gradle. Опять же IDEA умеет компилировать проекты сама, но на любом более менее сложном нужна утилита для сборки.
Объем: build lifecycle, управление зависимостями, многомодульные проекты, управление настройками сборки, плагины и их прикручивание к нужному шагу сборки
6) docker. Мы идем в светлое облачное будущее, Docker - это его основа.
Что важно: зачем нужен Docker, Docker Registry, слоистость образов, структура Docker файла
to be continued
#java #tools #обучение
Продолжим с hard-skills, не связанными с основным языком программирования.
7) базовые навыки DevOps. DevOps есть в любой более менее современной компании. Любой разработчик с ним соприкасается. Понимать основы надо.
Что я понимаю под основами: зачем вообще нужен DevOps, подсказка - см. мем "Это работает на моей машине", различие между CI и CD, Infrastructure As Code, опыт работы с каким-то оркестратором, например Jenkins, опыт работы со утилитой версионирования БД, например, Liquibase, опыт работы с SonarQube. Вообще круто, если можешь писать джобы сборки или знаешь что-то из Сonfiguration management software, например, Ansible. Вообще полный список DevOps инструментов огромен, вот наверное самый известный - периодическая таблица https://digital.ai/devops-tools-periodic-table
8) k8s, он же kubernates. Выделил отдельно, хотя тоже можно считать его DevOps инструментом. Почему надо знать - см. пункт про Docker и облака. Как говорится - все там будем)
Что надо знать: что k8s нам дает: https://t.me/javaKotlinDevOps/6, Deployment\ReplicaSet\Pod и их отличия, labels и selectors, request\limits, liveness\readyness\startup probes, ConfigMap, Secret и их различия, Service, Ingress, облачная DNS, Storage, основы работы с kubectl. Если знаешь Istio или Openshift - плюс в карму)
9) HTTP. По факту основной протокол для интеграции сейчас. Что нужно знать: методы HTPP, коды ошибок, заголовки, cookie, HTTP keep-alive, изменения в HTTP/2, Basic Auth и ее альтернативы, основные принципы кэширования контента и используемые для этого заголовки, редиректы. Основные коды ошибок полезно помнить наизусть. Сюда же я бы отнес "маппинг" REST на HTTP: HTTP методы, коды ошибок, заголовки. Для Middle + ожидается понимание, как проходит TLS Handshake https://habr.com/ru/post/258285/
10) Security. Понимание основных типов уязвимостей и способов защиты от них:
а) XSS\CSRF https://habr.com/ru/company/oleg-bunin/blog/412855/
б) SQL Injection и другие типы Injection https://habr.com/ru/company/simplepay/blog/259047/ Вспомним недавний Log4Shell https://t.me/javaKotlinDevOps/4, принцип тот же.
в) DOS атаки и что можно сделать в коде и в архитектуре приложения для защиты от них: https://pvs-studio.com/en/blog/terms/6585/
Также рекомендую проект OWASP, в частности TOP10 уязвимостей https://owasp.org/Top10/
Пожалуй все, хватит)
Пишите если что забыл или наоборот - слишком много требований
#java #обучение
7) базовые навыки DevOps. DevOps есть в любой более менее современной компании. Любой разработчик с ним соприкасается. Понимать основы надо.
Что я понимаю под основами: зачем вообще нужен DevOps, подсказка - см. мем "Это работает на моей машине", различие между CI и CD, Infrastructure As Code, опыт работы с каким-то оркестратором, например Jenkins, опыт работы со утилитой версионирования БД, например, Liquibase, опыт работы с SonarQube. Вообще круто, если можешь писать джобы сборки или знаешь что-то из Сonfiguration management software, например, Ansible. Вообще полный список DevOps инструментов огромен, вот наверное самый известный - периодическая таблица https://digital.ai/devops-tools-periodic-table
8) k8s, он же kubernates. Выделил отдельно, хотя тоже можно считать его DevOps инструментом. Почему надо знать - см. пункт про Docker и облака. Как говорится - все там будем)
Что надо знать: что k8s нам дает: https://t.me/javaKotlinDevOps/6, Deployment\ReplicaSet\Pod и их отличия, labels и selectors, request\limits, liveness\readyness\startup probes, ConfigMap, Secret и их различия, Service, Ingress, облачная DNS, Storage, основы работы с kubectl. Если знаешь Istio или Openshift - плюс в карму)
9) HTTP. По факту основной протокол для интеграции сейчас. Что нужно знать: методы HTPP, коды ошибок, заголовки, cookie, HTTP keep-alive, изменения в HTTP/2, Basic Auth и ее альтернативы, основные принципы кэширования контента и используемые для этого заголовки, редиректы. Основные коды ошибок полезно помнить наизусть. Сюда же я бы отнес "маппинг" REST на HTTP: HTTP методы, коды ошибок, заголовки. Для Middle + ожидается понимание, как проходит TLS Handshake https://habr.com/ru/post/258285/
10) Security. Понимание основных типов уязвимостей и способов защиты от них:
а) XSS\CSRF https://habr.com/ru/company/oleg-bunin/blog/412855/
б) SQL Injection и другие типы Injection https://habr.com/ru/company/simplepay/blog/259047/ Вспомним недавний Log4Shell https://t.me/javaKotlinDevOps/4, принцип тот же.
в) DOS атаки и что можно сделать в коде и в архитектуре приложения для защиты от них: https://pvs-studio.com/en/blog/terms/6585/
Также рекомендую проект OWASP, в частности TOP10 уязвимостей https://owasp.org/Top10/
Пожалуй все, хватит)
Пишите если что забыл или наоборот - слишком много требований
#java #обучение
Digital.ai
Periodic Table of DevSecOps Tools | Digital.ai
The Periodic Table of DevSecOps Tools is the industry's go-to resource for identifying best-of-breed tools across the software delivery lifecycle.
Всем привет!
В предыдущем посте я упомянул про защиту от DOS аттак в коде. Раскрою тему.
Для начала стоит различать DDOS и DOS - (Distribted) Denial of Service.
Первый - это когда злоумышленник долбит миллионами запросов в секунду. Такое не выдержит ни один сервис, не поможет даже k8s, т.к. ресурсы кластера не резиновые - https://kubernetes.io/docs/setup/best-practices/cluster-large/ да и подымаются новые ноды не мгновенно. Следовательно, от DDOS должна защищать сетевая инфраструктура, прикладной разработчик тут ничего сделать не может.
Другое дело DOS - RPS на порядки меньше, эксплуатируются уязвимости в коде. Вопрос - откуда злоумышленники про них узнают?
Во-первых они могут действовать наугад, во-вторых - всегда могут быть болтливые сотрудники, а главное - защита типа "об этом никто никогда не узнает" - плохая защиты.
Суть всех уязвимостией при DOS одна - поднять на сервере столько потоков одновременно, чтобы закончилась память или загрузка процессора ушла под 100%
Итак, как можно улучшить код для защиты от DOS.
1) проводить нагрузочное тестирование (НТ). НТ позволяет точно определеить сколько нужно серверов, чтобы держать расчетную и пиковую нагрузку. Пиковую нагрузку можно взять как расчетная умножить на два. Утечки памяти, неоптимальный код - все это с большой вероятностью можно увидеть на НТ
2) нет бесконечным и большим таймаутам. Если смежник упал, а у нас бесконечный таймаут - потоки и память кончатся быстро. Что касается больших таймаутов - это минуты, или таймауты необоснованные с точки зрения бизнес-задачи.
2) таймауты должны быть согласованы. Если мы обрабатываем запрос с таймутом 5 секунд, он синхронный, а вызываем смежника с таймутом 10 секунд - мы зря тратим его и свои ресурсы. Согласование может быть ручным, либо можно слать, например в заголовках, свой таймаут смежнику, чтобы он не ждал зря.
3) использовать circuit breaker, он же предохранитель, он же техперерыв. Если известно, что смежная система прилегла - не надо ее добивать и тратить на это свои ресурсы. Берем данные из кэша если это возможно или возвращаем клиенту ошибку. Принцип fail fast. Стоит отметить, что настройку таймаутов, предохранителя, и числа повторов можно делать либо в коде, либо отдать на откуп Istio или аналогичной системе если мы в облаке. Что лучше - это отдельная тема
4) защищаться от уязвимостей типа Injection. Суть их в том, что злоумышленник передает в параметрах входящего запроса что-то, что приводит к нелинейному потреблению ресуросов или тяжелым запросам в БД. Примеры первого вида DTD схемы https://habr.com/ru/post/170333/, регулярки - https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS, второго - SQL Injection со сложными JOIN. Решение: валидация параметров, регулярно сканировать библиотеки на наличие уязвимостей, регулярно обновляться в части хотфиксов
5) логика сервиса не должна линейно зависеть от числа входных параметров либо число параметров должно ограничено. Чем-то похоже на предыдущий пункт, но тут приложение спроектировано криво, поэтому никакие уязвимости не нужны)
6) использовать пулы потоков. Во многих случаях они уже используются - обработка входящих веб-запросов, JDBC запросы к БД. Но есть потоки, которые создаем мы сами. Если на каждый входной запрос мы будем создавать дополнительно хотя бы +1 поток, то это примерно удвоит потребление ресурсов. А если больше одного... Пул потоков защищает от такой ситуации
7) не забывать закрывать ресурсы - файлы, коннекты к БД. Все что java.io.Closeable. И делать это правильно - try with resources. В отличие от памяти в куче ресурсы никто за вас не закроет. А они жрут память и часто ограничены: максимальное число открытых файлов в Linux, максимальное число запросов, которое может обрабатывать СУБД
8) не использовать тяжелые JOIN и GROUP BY запросы к БД. Создавать индексы, смотреть план выполнения запроса. Об этом должен позаботиться ваш DBA, но, увы, не всегда он есть
9) не использовать излишне сильные уровни блокировки в БД, не использовать блокировки файлов без явной необходимости
#code_quality #security #patterns
В предыдущем посте я упомянул про защиту от DOS аттак в коде. Раскрою тему.
Для начала стоит различать DDOS и DOS - (Distribted) Denial of Service.
Первый - это когда злоумышленник долбит миллионами запросов в секунду. Такое не выдержит ни один сервис, не поможет даже k8s, т.к. ресурсы кластера не резиновые - https://kubernetes.io/docs/setup/best-practices/cluster-large/ да и подымаются новые ноды не мгновенно. Следовательно, от DDOS должна защищать сетевая инфраструктура, прикладной разработчик тут ничего сделать не может.
Другое дело DOS - RPS на порядки меньше, эксплуатируются уязвимости в коде. Вопрос - откуда злоумышленники про них узнают?
Во-первых они могут действовать наугад, во-вторых - всегда могут быть болтливые сотрудники, а главное - защита типа "об этом никто никогда не узнает" - плохая защиты.
Суть всех уязвимостией при DOS одна - поднять на сервере столько потоков одновременно, чтобы закончилась память или загрузка процессора ушла под 100%
Итак, как можно улучшить код для защиты от DOS.
1) проводить нагрузочное тестирование (НТ). НТ позволяет точно определеить сколько нужно серверов, чтобы держать расчетную и пиковую нагрузку. Пиковую нагрузку можно взять как расчетная умножить на два. Утечки памяти, неоптимальный код - все это с большой вероятностью можно увидеть на НТ
2) нет бесконечным и большим таймаутам. Если смежник упал, а у нас бесконечный таймаут - потоки и память кончатся быстро. Что касается больших таймаутов - это минуты, или таймауты необоснованные с точки зрения бизнес-задачи.
2) таймауты должны быть согласованы. Если мы обрабатываем запрос с таймутом 5 секунд, он синхронный, а вызываем смежника с таймутом 10 секунд - мы зря тратим его и свои ресурсы. Согласование может быть ручным, либо можно слать, например в заголовках, свой таймаут смежнику, чтобы он не ждал зря.
3) использовать circuit breaker, он же предохранитель, он же техперерыв. Если известно, что смежная система прилегла - не надо ее добивать и тратить на это свои ресурсы. Берем данные из кэша если это возможно или возвращаем клиенту ошибку. Принцип fail fast. Стоит отметить, что настройку таймаутов, предохранителя, и числа повторов можно делать либо в коде, либо отдать на откуп Istio или аналогичной системе если мы в облаке. Что лучше - это отдельная тема
4) защищаться от уязвимостей типа Injection. Суть их в том, что злоумышленник передает в параметрах входящего запроса что-то, что приводит к нелинейному потреблению ресуросов или тяжелым запросам в БД. Примеры первого вида DTD схемы https://habr.com/ru/post/170333/, регулярки - https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS, второго - SQL Injection со сложными JOIN. Решение: валидация параметров, регулярно сканировать библиотеки на наличие уязвимостей, регулярно обновляться в части хотфиксов
5) логика сервиса не должна линейно зависеть от числа входных параметров либо число параметров должно ограничено. Чем-то похоже на предыдущий пункт, но тут приложение спроектировано криво, поэтому никакие уязвимости не нужны)
6) использовать пулы потоков. Во многих случаях они уже используются - обработка входящих веб-запросов, JDBC запросы к БД. Но есть потоки, которые создаем мы сами. Если на каждый входной запрос мы будем создавать дополнительно хотя бы +1 поток, то это примерно удвоит потребление ресурсов. А если больше одного... Пул потоков защищает от такой ситуации
7) не забывать закрывать ресурсы - файлы, коннекты к БД. Все что java.io.Closeable. И делать это правильно - try with resources. В отличие от памяти в куче ресурсы никто за вас не закроет. А они жрут память и часто ограничены: максимальное число открытых файлов в Linux, максимальное число запросов, которое может обрабатывать СУБД
8) не использовать тяжелые JOIN и GROUP BY запросы к БД. Создавать индексы, смотреть план выполнения запроса. Об этом должен позаботиться ваш DBA, но, увы, не всегда он есть
9) не использовать излишне сильные уровни блокировки в БД, не использовать блокировки файлов без явной необходимости
#code_quality #security #patterns
Kubernetes
Considerations for large clusters
A cluster is a set of nodes (physical or virtual machines) running Kubernetes agents, managed by the control plane. Kubernetes v1.33 supports clusters with up to 5,000 nodes. More specifically, Kubernetes is designed to accommodate configurations that meet…
Всем привет!
Помимо знания языка программирования и инструментов, о которых я писал в предыдущих постах, есть еще один важный навык, который часто недооценивают. Я про знание среды разработки, она же IDE. Это инструмент, с которым мы работаем каждый день. Позволяет автоматизировать большое количество рутинных действий и съэкономить кучу времени.
Предположу, что у большинства Java разработчиков IDE - это IntelliJ IDEA. Бесплатная Community или платная Ultimate.
Да, конечно, существуют Netbeans и Eclipse. У первой сложная судьба - начинала как "придворная" IDE в Sun\Oracle, а заканчивает как OpenSource в Apache Foundation.
Вторая - изначально OpenSource, с акцентом на расширяемость плагинами, довольно активно развивается. Еще Eclipse - живой пример использования OSGi. Но есть нюанс. На мой взгляд любой успешный (!) коммерческий продукт, создатели которого не продали душу богу маркетинга (!) - т.е. не соблазнились свистелками, а вместо этого думают о пользователе - будет бить OpenSource за счет возможности координации усилий. Приведу примеры. Вышла новая версия Java - оперативно добавили поддержку. Популярен сейчас Kotlin или новый фреймворк - наняли людей, перебросили команду и добавили поддержку. Поняли, что можно упростить жизнь разработчику готовыми инструментами для рефакторинга - составили список и автоматизировали. Скоордировать сообщество разработчиков, для которых OpenSource - хобби, для быстрого решения подобных задач сложнее. Хотя наверное при определенном количестве commiters - возможно.
Но я отвлекся. Сегодня речь про IDEA. IDEA может многое. Пречисленные ниже фичи в разное время меня впечатлили, и я считаю на них стоит обратить внимание:
- автодополнение: по идущим не подряд символам из названия метода набранным в нужном порядке, по символам в неверной раскладке, с сортировкой исходя из машинного обучения. Да, да ИИ уже с нами)
https://www.jetbrains.com/help/idea/auto-completing-code.html
- автогенерация: тесты к классу, override, конструкторы, getter и setter https://www.jetbrains.com/help/idea/generating-code.html
- огромное число рефакторингов, причем достаточно умных. Пример умного рефакторинга: при переименовании IDEA отдельно спрашивает подтверждение на совпадения в комментариях. https://www.jetbrains.com/help/idea/refactoring-source-code.html
- всеобъемлющий поиск: по настройкам, по классам, файлам, действиям в IDE, и по всему сразу, поиск использования класса\метода, по коду проекта и поиск внутри файла, в т.ч с регуляркой, структурный поиск - найти все enum в проекте, поиск по методам класса.
Даже перечисление видов поиска заняло кучу места, а мой список не полон) А еще есть вкладки с предыдущими результатами поиска, самобновляющиеся после редактирования кода.
https://www.jetbrains.com/help/idea/finding-and-replacing-text-in-file.html
- отличная интеграция с Git. И скорее всего с другими VCS, но я тестировал только Git. Фичи: удобно выбирать файлы и даже строки для коммита, на коммит можно повесить проверки, простые Revert, Merge, Amend commit, сравнения файлов. В целом для 99% процентов задач можно забыть про сторонние клиенты Git.
https://www.jetbrains.com/help/idea/version-control-integration.html
- рендеринг кода в редакторе: отображение "лямбд" еще до их появления в Java 7))), упрощенное представление лямбд, компактный JavaDoc, работа с секциями
- подсказки по коду: краткая и полная информация о классе или методе, всплывающий JavaDoc, подкачивание исходников при их наличии. https://www.jetbrains.com/help/idea/viewing-reference-information.html
- анализ зависимостей между классами, в т.ч в виде матрицы. Позволяет распутывать клубки в больших проектах. https://www.jetbrains.com/help/idea/dependencies-analysis.html
- возможность скачивания JDK разных версий и вендоров прямо из IDE
- HTTP client
- Code with Me - возможность совместной работы, хотя я ее так и не оттестировал(
- анализ стектрейсов, полученных от тестировщиков или сопровождения, переход к коду из стэктрейса https://www.jetbrains.com/help/idea/analyzing-external-stacktraces.html
Помимо знания языка программирования и инструментов, о которых я писал в предыдущих постах, есть еще один важный навык, который часто недооценивают. Я про знание среды разработки, она же IDE. Это инструмент, с которым мы работаем каждый день. Позволяет автоматизировать большое количество рутинных действий и съэкономить кучу времени.
Предположу, что у большинства Java разработчиков IDE - это IntelliJ IDEA. Бесплатная Community или платная Ultimate.
Да, конечно, существуют Netbeans и Eclipse. У первой сложная судьба - начинала как "придворная" IDE в Sun\Oracle, а заканчивает как OpenSource в Apache Foundation.
Вторая - изначально OpenSource, с акцентом на расширяемость плагинами, довольно активно развивается. Еще Eclipse - живой пример использования OSGi. Но есть нюанс. На мой взгляд любой успешный (!) коммерческий продукт, создатели которого не продали душу богу маркетинга (!) - т.е. не соблазнились свистелками, а вместо этого думают о пользователе - будет бить OpenSource за счет возможности координации усилий. Приведу примеры. Вышла новая версия Java - оперативно добавили поддержку. Популярен сейчас Kotlin или новый фреймворк - наняли людей, перебросили команду и добавили поддержку. Поняли, что можно упростить жизнь разработчику готовыми инструментами для рефакторинга - составили список и автоматизировали. Скоордировать сообщество разработчиков, для которых OpenSource - хобби, для быстрого решения подобных задач сложнее. Хотя наверное при определенном количестве commiters - возможно.
Но я отвлекся. Сегодня речь про IDEA. IDEA может многое. Пречисленные ниже фичи в разное время меня впечатлили, и я считаю на них стоит обратить внимание:
- автодополнение: по идущим не подряд символам из названия метода набранным в нужном порядке, по символам в неверной раскладке, с сортировкой исходя из машинного обучения. Да, да ИИ уже с нами)
https://www.jetbrains.com/help/idea/auto-completing-code.html
- автогенерация: тесты к классу, override, конструкторы, getter и setter https://www.jetbrains.com/help/idea/generating-code.html
- огромное число рефакторингов, причем достаточно умных. Пример умного рефакторинга: при переименовании IDEA отдельно спрашивает подтверждение на совпадения в комментариях. https://www.jetbrains.com/help/idea/refactoring-source-code.html
- всеобъемлющий поиск: по настройкам, по классам, файлам, действиям в IDE, и по всему сразу, поиск использования класса\метода, по коду проекта и поиск внутри файла, в т.ч с регуляркой, структурный поиск - найти все enum в проекте, поиск по методам класса.
Даже перечисление видов поиска заняло кучу места, а мой список не полон) А еще есть вкладки с предыдущими результатами поиска, самобновляющиеся после редактирования кода.
https://www.jetbrains.com/help/idea/finding-and-replacing-text-in-file.html
- отличная интеграция с Git. И скорее всего с другими VCS, но я тестировал только Git. Фичи: удобно выбирать файлы и даже строки для коммита, на коммит можно повесить проверки, простые Revert, Merge, Amend commit, сравнения файлов. В целом для 99% процентов задач можно забыть про сторонние клиенты Git.
https://www.jetbrains.com/help/idea/version-control-integration.html
- рендеринг кода в редакторе: отображение "лямбд" еще до их появления в Java 7))), упрощенное представление лямбд, компактный JavaDoc, работа с секциями
- подсказки по коду: краткая и полная информация о классе или методе, всплывающий JavaDoc, подкачивание исходников при их наличии. https://www.jetbrains.com/help/idea/viewing-reference-information.html
- анализ зависимостей между классами, в т.ч в виде матрицы. Позволяет распутывать клубки в больших проектах. https://www.jetbrains.com/help/idea/dependencies-analysis.html
- возможность скачивания JDK разных версий и вендоров прямо из IDE
- HTTP client
- Code with Me - возможность совместной работы, хотя я ее так и не оттестировал(
- анализ стектрейсов, полученных от тестировщиков или сопровождения, переход к коду из стэктрейса https://www.jetbrains.com/help/idea/analyzing-external-stacktraces.html
IntelliJ IDEA Help
Code completion | IntelliJ IDEA
Techniques to speed up the editing process in IntelliJ IDEA using code completion (basic completion, smart
completion based on a type, hippie expand, and so on).
completion based on a type, hippie expand, and so on).
- быстрое получение ссылки на код: сopy path или сopy reference, а с внешними плагинами и Copy Bitbucket Link
- shortcuts практически для всего: https://www.jetbrains.com/help/idea/reference-keymap-win-default.html А для действий без стандартных shortcuts можно назначить свои, причем не только клавиатурные, но и мышинные)
Рекомендую начинать учить, если еще не сделали.
Начать можно с https://blog.jetbrains.com/idea/2020/03/top-15-intellij-idea-shortcuts/
Также не стоит выключать обучающий режим, когда при открытии проекта IDEA показывает полезные советы, в т.ч. shortcuts.
- разнообразие внешних плагинов, рекомендую заглянуть в соответствующий раздел настроек.
В общем IDEA - это целый мир, в который можно и нужно погрузится с головой.
Возможно я что-то упустил из крутых или важных фичей, напишите об этом.
#IDEA #IDE #tools #обучение
- shortcuts практически для всего: https://www.jetbrains.com/help/idea/reference-keymap-win-default.html А для действий без стандартных shortcuts можно назначить свои, причем не только клавиатурные, но и мышинные)
Рекомендую начинать учить, если еще не сделали.
Начать можно с https://blog.jetbrains.com/idea/2020/03/top-15-intellij-idea-shortcuts/
Также не стоит выключать обучающий режим, когда при открытии проекта IDEA показывает полезные советы, в т.ч. shortcuts.
- разнообразие внешних плагинов, рекомендую заглянуть в соответствующий раздел настроек.
В общем IDEA - это целый мир, в который можно и нужно погрузится с головой.
Возможно я что-то упустил из крутых или важных фичей, напишите об этом.
#IDEA #IDE #tools #обучение
IntelliJ IDEA Help
Predefined Windows keymap | IntelliJ IDEA
Всем привет!
Наверняка многие слышали про практику TDD - Test Driven Development. У которой есть некие сложности с применением. Напомню, ее суть не только и не столько в том, что тест должен предшествовать коду, а в сокращении врени итерации - тест-код. Т.е. в идеальном случае надо делать так:
1) простейший тест
2) код с заглушкой, позволяющий пройти тест
3) новый тест
4) усложнение заглушки
...
N) финальный работающий код
И коммитить желательно почаще.
Самое сложное здесь - заставить себя не писать сразу кучу тестов или весь метод сразу. Или даже весь класс + ряд зависимых классов, а идти мелкими шагами.
Сам пробовал - сложно)
И от одного из создателей XP Кента Бека есть решение - TCR https://medium.com/@kentbeck_7670/test-commit-revert-870bbd756864
Расшифровывается как test && commit | | revert.
Суть в следующем:
1) к запуску тестов привязывается команда git
2) если тесты успешные - git commit
3) если тесты упали, то коммитить неработающий код нельзя, потому делаем revert в виде git reset --hard
Только хардкор!)
4) в паралльном потоке работает цикл
while (true) {
git pull --rebase
git push
}
синхронизируя изменения других разработчиков.
Итог: переписав несколько раз большие куски кода из-за падения теста рано или поздно длина итераций станет минимально возможной.
Второй возможный кейс - кусок кода небольшой, но commit вовремя не сделан, и кто-то что-то параллельно закоммитил в develop и поломал ваши тесты.
Пример использования на практике с реализацией shell скрипта "test && commit | | revert":
https://medium.com/@tdeniffel/tcr-test-commit-revert-a-test-alternative-to-tdd-6e6b03c22bec
Что думаете?
#Agile #XP #TDD #TCR
Наверняка многие слышали про практику TDD - Test Driven Development. У которой есть некие сложности с применением. Напомню, ее суть не только и не столько в том, что тест должен предшествовать коду, а в сокращении врени итерации - тест-код. Т.е. в идеальном случае надо делать так:
1) простейший тест
2) код с заглушкой, позволяющий пройти тест
3) новый тест
4) усложнение заглушки
...
N) финальный работающий код
И коммитить желательно почаще.
Самое сложное здесь - заставить себя не писать сразу кучу тестов или весь метод сразу. Или даже весь класс + ряд зависимых классов, а идти мелкими шагами.
Сам пробовал - сложно)
И от одного из создателей XP Кента Бека есть решение - TCR https://medium.com/@kentbeck_7670/test-commit-revert-870bbd756864
Расшифровывается как test && commit | | revert.
Суть в следующем:
1) к запуску тестов привязывается команда git
2) если тесты успешные - git commit
3) если тесты упали, то коммитить неработающий код нельзя, потому делаем revert в виде git reset --hard
Только хардкор!)
4) в паралльном потоке работает цикл
while (true) {
git pull --rebase
git push
}
синхронизируя изменения других разработчиков.
Итог: переписав несколько раз большие куски кода из-за падения теста рано или поздно длина итераций станет минимально возможной.
Второй возможный кейс - кусок кода небольшой, но commit вовремя не сделан, и кто-то что-то параллельно закоммитил в develop и поломал ваши тесты.
Пример использования на практике с реализацией shell скрипта "test && commit | | revert":
https://medium.com/@tdeniffel/tcr-test-commit-revert-a-test-alternative-to-tdd-6e6b03c22bec
Что думаете?
#Agile #XP #TDD #TCR
Medium
test && commit || revert
As part of Limbo on the Cheap, we invented a new programming workflow. I introduced “test && commit”, where every time the tests run…
Всем привет!
Чтобы закончить тему с TDD, ну хотя бы на ближайшее время закончить, хочу подчеркнуть его плюсы и порекомендовать книгу.
Плюсы:
1) точно достигните требуемого покрытия кода тестами. Мне сложно представить откуда может взяться непокрытый тестами код если приложение создано по TDD начиная с первой строчки
2) писать тесты будет не так тяжело, как при типичном подходе. Когда код написан, отлажен, возможно даже прошел функциональное тестирование, а вместо создания чего-то нового или обучения приходится удовлетворять SonarQube и требования организации\тимлида - это не весело))) А тут еще выясняется, что код плохо приспособлен для тестирования. И кто знает, возможно с TDD даже появятся позитивные эмоциии, особенно когда тесты зеленеют)
3) меньше шансов, что приложение, даже если оно изначально разрабатывалось как микросервис, превратится в монолит(ик), который страшно трогать руками. Железо не гибкое, процессы и люди .. ну под вопросом. А ПО должно быть гибким.
4) не нужно объяснять PO что такое рефакторинг и почему для него выделяется столько много storypoins или человекодней. Т.к. рефакторинг станет неотрывной частью процесса разработки и затраты на него войдут в трудоемкость разработки фичи
5) полученные тесты - хороший аргумент, чтобы оставить в JavaDoc и аналитике только вещи, касающиеся описания бизнес-процесса, e2e, интеграций, т.к. все остальное будет в тестах. Причем документация с помощью тестов может разойтись с кодом только в одном случае - если на тесты забить) В отличие от.
6) возможно реализация логики улучшится. Как это может произойти. Часто в коде реализуется первая пришедшая в голову идея. А уже по результатам функционального тестирования она переписывается или что хуже запиливаюстя костыли. Это плохой подход - брать первую попавшуюся идею, но психологически бороться с ним сложно. Если же эволюционно писать тесты-код-тесты-код.. то быстрее можно прийти к пониманию, что первая реализация - не лучшая. А переписывание кода и тестов в TDD - это ок
Ну и книга. Это не Кент Бек "Экстремальное программирование: разработка через тестирование" https://habr.com/ru/company/piter/blog/326662/ как можно было подумать. Хотя и ее тоже можно порекомендовать, классика как никак.
Но недавно вышла новая книга Боба Мартина, он же «дядюшка» Боб "Идеальная работа. Программирование без прикрас" https://habr.com/ru/company/piter/blog/679378/. Первая часть книги как раз рассказывает как влится в TDD. Даже видосы есть с реализацией разных простых алгоритмов по TDD подходу.
Мне очень зашло, рекомендую, книгу вместе с видосами. Да, если вступление к книге покажется слишком официозным - переходите сразу к первой части.
Если книжки, статьи на разные темы в рамках Java\Kotlin\DevOps интересны - пишите, буду рекомендовать.
#books #tdd
Чтобы закончить тему с TDD, ну хотя бы на ближайшее время закончить, хочу подчеркнуть его плюсы и порекомендовать книгу.
Плюсы:
1) точно достигните требуемого покрытия кода тестами. Мне сложно представить откуда может взяться непокрытый тестами код если приложение создано по TDD начиная с первой строчки
2) писать тесты будет не так тяжело, как при типичном подходе. Когда код написан, отлажен, возможно даже прошел функциональное тестирование, а вместо создания чего-то нового или обучения приходится удовлетворять SonarQube и требования организации\тимлида - это не весело))) А тут еще выясняется, что код плохо приспособлен для тестирования. И кто знает, возможно с TDD даже появятся позитивные эмоциии, особенно когда тесты зеленеют)
3) меньше шансов, что приложение, даже если оно изначально разрабатывалось как микросервис, превратится в монолит(ик), который страшно трогать руками. Железо не гибкое, процессы и люди .. ну под вопросом. А ПО должно быть гибким.
4) не нужно объяснять PO что такое рефакторинг и почему для него выделяется столько много storypoins или человекодней. Т.к. рефакторинг станет неотрывной частью процесса разработки и затраты на него войдут в трудоемкость разработки фичи
5) полученные тесты - хороший аргумент, чтобы оставить в JavaDoc и аналитике только вещи, касающиеся описания бизнес-процесса, e2e, интеграций, т.к. все остальное будет в тестах. Причем документация с помощью тестов может разойтись с кодом только в одном случае - если на тесты забить) В отличие от.
6) возможно реализация логики улучшится. Как это может произойти. Часто в коде реализуется первая пришедшая в голову идея. А уже по результатам функционального тестирования она переписывается или что хуже запиливаюстя костыли. Это плохой подход - брать первую попавшуюся идею, но психологически бороться с ним сложно. Если же эволюционно писать тесты-код-тесты-код.. то быстрее можно прийти к пониманию, что первая реализация - не лучшая. А переписывание кода и тестов в TDD - это ок
Ну и книга. Это не Кент Бек "Экстремальное программирование: разработка через тестирование" https://habr.com/ru/company/piter/blog/326662/ как можно было подумать. Хотя и ее тоже можно порекомендовать, классика как никак.
Но недавно вышла новая книга Боба Мартина, он же «дядюшка» Боб "Идеальная работа. Программирование без прикрас" https://habr.com/ru/company/piter/blog/679378/. Первая часть книги как раз рассказывает как влится в TDD. Даже видосы есть с реализацией разных простых алгоритмов по TDD подходу.
Мне очень зашло, рекомендую, книгу вместе с видосами. Да, если вступление к книге покажется слишком официозным - переходите сразу к первой части.
Если книжки, статьи на разные темы в рамках Java\Kotlin\DevOps интересны - пишите, буду рекомендовать.
#books #tdd
Хабр
Книга «Экстремальное программирование: разработка через тестирование»
Возвращение знаменитого бестселлера. Изящный, гибкий и понятный код, который легко модифицировать, который корректно работает и который не подкидывает своим создателям неприятных сюрпризов. Неужели...
Всем привет!
Каким должен быть хороший тест?
Я в первую очередь про модульные (unit), но в принципе правила применимы к любым.
Основные моменты:
1) правило Arrange, Act, Assert https://xp123.com/articles/3a-arrange-act-assert/
Тест делится на три части: подготовка тестовых данных, вызов тестового метода и проверка. Часто забывают про последнюю.
Тест должен что-то проверить: выброшенное исключение, сколько и каких было вызвано методов, состояние объекта.
Тест проверяющий только тот факт, что вызов прошел без исключения, добавляет покрытия, но по сути является недотестом. Его успех показывает, что что-то там выполнилось) Выполнено ли то, что должен делать метод - не ясно. Если API не позволяет проверить результат выполнения кода - это плохое API. Если же код legacy и рефакторить его сложно - можно воспользоваться Mockito.spy или рефлексией.
2) тестовый код - такой же код, как и боевой. К нему должны быть применены все практики написания чистого кода - https://www.litres.ru/robert-s-martin/chistyy-kod-sozdanie-analiz-i-refaktoring-6444478
Я вижу одно исключение - в тестовом коде норм использовать System.out.println
3) тест должен проверять одну операцию с одним или несколькими связанными наборами входных параметров, это удобно делать через параметризацию. При этом допустимо в тесте использовать несколько Assert. Хотя можно их вынести в один assert метод. Или использовать Soft Assert http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#soft-assertions. Как по мне - все три варианта норм, дело вкуса.
4) все внешние объекты, требуемые тестируемому методу, должны быть заглушены. Наиболее удобно использовать Mockito.mock или Mockito.spy, но если надо - можно наследоваться от интерфейса и сделать тестовый двойник самому. Если глушить приходится слишком много - повод задуматься про архитектуру кода
5) если стандартного API Mockito или самописных заглушек не хватает, и руки тянутся к включить "секретные" опции Mockito для того, чтобы заглушить private, static или наследоваться от final класса - тоже повод задуматься про архитектуру. Хотя как быстрое решение для legacy допустимо.
6) в поставке для ПРОМа не должно быть тестового кода, боевой код не должен использовать тестовые зависимости и Helpers. В боевом коде не должно быть "ловушек" для успешного выполнения тестов - т.е. выражений типа
if (isTestRun()) {
7) тесты - это документация к коду. Для этого тест должен легко читаться. А чтобы этого добиться - нужно выносить все лишние во вспомогательные методы для Arrange и Assert, передавая как параметры в эти методы только то, что непосредственно влияет на конкретный тест. В этом плане при выборе между @ BeforeEach методом и обычным нужно выбирать обычный - так замысел теста легче читается.
А при возникновении вопроса чтобы бы добавить в тесты, или может уже хватит, стоит в первую очередь думать о закрытии всех основных комбинаций входных параметров, а не о покрытии. Требуемый процент покрытия можно подкрутить, главное как и у тестировщиков - полнота тестовой модели. И читаемость тестов
8) тесты должны быть антихрупкими. Т.е. тест не должен падать при любых правках кода, а тем более - при правках кода, который на первый взгляд никак не связан с тестом. Задача теста - облегчить рефакторинг и доработки, а не усложнить их) Готовых рецептов тут нет, но если данные для assert меняются каждый релиз, значить проверять надо что-то еще. Пример: если речь про API - атрибуты JSON, а не полный JSON
9) тесты не должны зависеть от результата других тестов, от порядка выполнения и от среды
10) из логов запуска упавшего теста должно быть понятно, в чем ошибка. Для этого тест должен выполняться быстро, чтобы запускать его после каждого изменения, тестовые методы должны быть названы осознано, и самое сложное - в assert-ах должны быть сообщения об ошибках. И \ или использовать power asserts, которые подробно выводят в лог что пришло в Assert метод https://github.com/bnorm/kotlin-power-assert
P.S. Женя Осмаковский, спасибо за подсказку по п.8
#unittests #cleancode
Каким должен быть хороший тест?
Я в первую очередь про модульные (unit), но в принципе правила применимы к любым.
Основные моменты:
1) правило Arrange, Act, Assert https://xp123.com/articles/3a-arrange-act-assert/
Тест делится на три части: подготовка тестовых данных, вызов тестового метода и проверка. Часто забывают про последнюю.
Тест должен что-то проверить: выброшенное исключение, сколько и каких было вызвано методов, состояние объекта.
Тест проверяющий только тот факт, что вызов прошел без исключения, добавляет покрытия, но по сути является недотестом. Его успех показывает, что что-то там выполнилось) Выполнено ли то, что должен делать метод - не ясно. Если API не позволяет проверить результат выполнения кода - это плохое API. Если же код legacy и рефакторить его сложно - можно воспользоваться Mockito.spy или рефлексией.
2) тестовый код - такой же код, как и боевой. К нему должны быть применены все практики написания чистого кода - https://www.litres.ru/robert-s-martin/chistyy-kod-sozdanie-analiz-i-refaktoring-6444478
Я вижу одно исключение - в тестовом коде норм использовать System.out.println
3) тест должен проверять одну операцию с одним или несколькими связанными наборами входных параметров, это удобно делать через параметризацию. При этом допустимо в тесте использовать несколько Assert. Хотя можно их вынести в один assert метод. Или использовать Soft Assert http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#soft-assertions. Как по мне - все три варианта норм, дело вкуса.
4) все внешние объекты, требуемые тестируемому методу, должны быть заглушены. Наиболее удобно использовать Mockito.mock или Mockito.spy, но если надо - можно наследоваться от интерфейса и сделать тестовый двойник самому. Если глушить приходится слишком много - повод задуматься про архитектуру кода
5) если стандартного API Mockito или самописных заглушек не хватает, и руки тянутся к включить "секретные" опции Mockito для того, чтобы заглушить private, static или наследоваться от final класса - тоже повод задуматься про архитектуру. Хотя как быстрое решение для legacy допустимо.
6) в поставке для ПРОМа не должно быть тестового кода, боевой код не должен использовать тестовые зависимости и Helpers. В боевом коде не должно быть "ловушек" для успешного выполнения тестов - т.е. выражений типа
if (isTestRun()) {
7) тесты - это документация к коду. Для этого тест должен легко читаться. А чтобы этого добиться - нужно выносить все лишние во вспомогательные методы для Arrange и Assert, передавая как параметры в эти методы только то, что непосредственно влияет на конкретный тест. В этом плане при выборе между @ BeforeEach методом и обычным нужно выбирать обычный - так замысел теста легче читается.
А при возникновении вопроса чтобы бы добавить в тесты, или может уже хватит, стоит в первую очередь думать о закрытии всех основных комбинаций входных параметров, а не о покрытии. Требуемый процент покрытия можно подкрутить, главное как и у тестировщиков - полнота тестовой модели. И читаемость тестов
8) тесты должны быть антихрупкими. Т.е. тест не должен падать при любых правках кода, а тем более - при правках кода, который на первый взгляд никак не связан с тестом. Задача теста - облегчить рефакторинг и доработки, а не усложнить их) Готовых рецептов тут нет, но если данные для assert меняются каждый релиз, значить проверять надо что-то еще. Пример: если речь про API - атрибуты JSON, а не полный JSON
9) тесты не должны зависеть от результата других тестов, от порядка выполнения и от среды
10) из логов запуска упавшего теста должно быть понятно, в чем ошибка. Для этого тест должен выполняться быстро, чтобы запускать его после каждого изменения, тестовые методы должны быть названы осознано, и самое сложное - в assert-ах должны быть сообщения об ошибках. И \ или использовать power asserts, которые подробно выводят в лог что пришло в Assert метод https://github.com/bnorm/kotlin-power-assert
P.S. Женя Осмаковский, спасибо за подсказку по п.8
#unittests #cleancode
Xp123
3A - Arrange, Act, Assert - XP123
Arrange-Act-Assert is a way to structure microtests - to make sure they clearly do one thing and aren't like a run-on-sentence.
Всем привет!
Пару слов о том, на что не нужно писать модульные тесты.
Женя, спасибо за вопросы к предыдущему посту!
Для начала очевидные кейсы:
1) getter и setter. Во-первый - логики там нет, точнее не должно быть. Более того эти методы скорее всего будут вызываны из других тестов. Более того, методы вообще говоря можно не писать руками, а сгенерировать с помощью Lombook @Getter и @Setter, или использовать Kotlin. Что касается покрытия, то как я уже писал ранее - покрытие не главная цель, скорее следствие. А кроме того Jacoco -как самый распространенный инструмент - не считает покрытие по сгенерированным методам Lombook. Детальнее про то, что не учитывается в покрытии и что можно донастроить см. https://www.baeldung.com/jacoco-report-exclude и https://github.com/jacoco/jacoco/wiki/filtering-JAVAC.SYNTHMETH
2) сгенерированный код. Причины те же, как исключить см. выше.
3) код внешних библиотек. Как первая версия теста при TDD подходе, чтобы проверить, что код вообще компилируется - да, можно. Чтобы убедиться, что JDK правильная в проекте, все библиотеки подключены. Проверить что новая библиотека работает как ожидаешь. Но потом такой тест или должен быть дополнен тестированием вашей логики, или удален. Код поднятия внешних библиотек при этом может быть вынесен в отдельный метод для переиспользования.
4) любой тривиальный код, типа конструкторы Entity\DTO или число элементов в Enum. В первом случае этот код должен быть вызван из других тестов. Во втором - не понятно, что доказывает этот тест.
Неоднозначные случаи:
5) корректноть инфраструктурного кода, типа создания контекста Spring. В идеале ее должен тестировать компилятор. Что он и сделает при использовании JavaConfig + Annotations. Если у вас XML Config - может помочь IDEA https://www.jetbrains.com/help/idea/spring-projects.html, ну или поднятие Spring контекста в других тестах.
6) методы-интеграторы: методы, единственная цель которых - вызывать другие методы. Как правило находятся в сервисах. Во-первых код теста получается хрупким, т.к. с новыми релизами добавляются новые вызовы. А во-вторых приходится писать кучу моков. А главное логики особо нет, кроме правильной последовательности вызовов и передачи данных по цепочке. Если появляется более сложная логика - валидация данных, проверка прав - тест будет иметь смысл. Иначе можно пропустить, а лучше написать интеграционный тест, включающий вызов такого метода-интегратора.
7) методы, конструирующие DTO для слоя API. DTO использовать нужно, чтобы развязать доменные сущности и API. Но такой код часто представляет просто mapping. С одной стороны какая-то практическая польза в такой проверке есть: данные должны быть в нужном формате - JSON, должны быть какие-то обязательные поля, нужно убедится, что данные из модели попали в ответ. С другой стороны если валидировать сравнением с эталонным JSON - тест получается хрупким. Решение - метод тестировать, но проверять результат преобразования точечно, фокусироваться на том, что скорее всего не изменится в будущем.
В заключение вот хорошая статья на тему написания правльных тестов https://habr.com/ru/company/jugru/blog/323920/
#unittests #Java
Пару слов о том, на что не нужно писать модульные тесты.
Женя, спасибо за вопросы к предыдущему посту!
Для начала очевидные кейсы:
1) getter и setter. Во-первый - логики там нет, точнее не должно быть. Более того эти методы скорее всего будут вызываны из других тестов. Более того, методы вообще говоря можно не писать руками, а сгенерировать с помощью Lombook @Getter и @Setter, или использовать Kotlin. Что касается покрытия, то как я уже писал ранее - покрытие не главная цель, скорее следствие. А кроме того Jacoco -как самый распространенный инструмент - не считает покрытие по сгенерированным методам Lombook. Детальнее про то, что не учитывается в покрытии и что можно донастроить см. https://www.baeldung.com/jacoco-report-exclude и https://github.com/jacoco/jacoco/wiki/filtering-JAVAC.SYNTHMETH
2) сгенерированный код. Причины те же, как исключить см. выше.
3) код внешних библиотек. Как первая версия теста при TDD подходе, чтобы проверить, что код вообще компилируется - да, можно. Чтобы убедиться, что JDK правильная в проекте, все библиотеки подключены. Проверить что новая библиотека работает как ожидаешь. Но потом такой тест или должен быть дополнен тестированием вашей логики, или удален. Код поднятия внешних библиотек при этом может быть вынесен в отдельный метод для переиспользования.
4) любой тривиальный код, типа конструкторы Entity\DTO или число элементов в Enum. В первом случае этот код должен быть вызван из других тестов. Во втором - не понятно, что доказывает этот тест.
Неоднозначные случаи:
5) корректноть инфраструктурного кода, типа создания контекста Spring. В идеале ее должен тестировать компилятор. Что он и сделает при использовании JavaConfig + Annotations. Если у вас XML Config - может помочь IDEA https://www.jetbrains.com/help/idea/spring-projects.html, ну или поднятие Spring контекста в других тестах.
6) методы-интеграторы: методы, единственная цель которых - вызывать другие методы. Как правило находятся в сервисах. Во-первых код теста получается хрупким, т.к. с новыми релизами добавляются новые вызовы. А во-вторых приходится писать кучу моков. А главное логики особо нет, кроме правильной последовательности вызовов и передачи данных по цепочке. Если появляется более сложная логика - валидация данных, проверка прав - тест будет иметь смысл. Иначе можно пропустить, а лучше написать интеграционный тест, включающий вызов такого метода-интегратора.
7) методы, конструирующие DTO для слоя API. DTO использовать нужно, чтобы развязать доменные сущности и API. Но такой код часто представляет просто mapping. С одной стороны какая-то практическая польза в такой проверке есть: данные должны быть в нужном формате - JSON, должны быть какие-то обязательные поля, нужно убедится, что данные из модели попали в ответ. С другой стороны если валидировать сравнением с эталонным JSON - тест получается хрупким. Решение - метод тестировать, но проверять результат преобразования точечно, фокусироваться на том, что скорее всего не изменится в будущем.
В заключение вот хорошая статья на тему написания правльных тестов https://habr.com/ru/company/jugru/blog/323920/
#unittests #Java
GitHub
filtering JAVAC.SYNTHMETH
:microscope: Java Code Coverage Library. Contribute to jacoco/jacoco development by creating an account on GitHub.