Всем привет!
Я слишком ударился в архитектуру в последних постах, возвращаюсь с небес на землю)
Уже был пост как правильно писать unit тесты https://t.me/javaKotlinDevOps/33 и https://t.me/javaKotlinDevOps/43. Но кажется, что не хватает примеров. Начнем с антипаттернов - что не нужно делать в тестах?
Есть такая всем известная в Java мире библиотека для создания заглушек как Mockito. И у него есть среди всего прочего такая конструкция как
doNothing().when(mock).someMethod();
На первый взгляд полезная штука - говорим, что при вызове определенного метода в "заглушенном" объекте ничего делать не надо.
Но копнув чуть глубже понимаешь, что результатом вызова
val mock = Mockito.mock(SomeClass.class);
как раз и является объект, который ничего не делает.
А точнее метод объекта не вызывается, а там, где нужно вернуть результат - возвращаются некие значения по умолчанию: https://site.mockito.org/javadoc/current/org/mockito/Mockito.html#2
А если так - явно писать doNothing() - это усложнять код теста, его читаемость и ценность как документации к коду. Держать в уме, что все методы заглушки, которые не переопределены в тесте, не делают ничего - проще и понятнее, чем явно перечислять их.
Теперь уже метод doNothing() выглядит бесполезным. Зачем же его тогда добавляли в библиотеку?
Есть два кейса:
1) когда вы используете spy, а не mock. Напомню, главное отличие spy - он по умолчанию вызывает методы существующего объекта.
Пример:
List list = new LinkedList();
List spy = spy(list);
doNothing().when(spy).clear();
spy.someMethodWithCLeaCall();
2) и об этой возможности я, признаюсь, не знал - в одном выражении можно задать несколько ответов на ряд последовательных вызовов одного метода заглушки:
doNothing()
.doThrow(new RuntimeException())
.when(mock).someVoidMethod();
//первый раз ничего не делаем
mock.someVoidMethod();
//бросаем RuntimeException во второй раз
mock.someVoidMethod();
В следующих постах будет продолжение...
#unittests #mockito #antipatterns
Я слишком ударился в архитектуру в последних постах, возвращаюсь с небес на землю)
Уже был пост как правильно писать unit тесты https://t.me/javaKotlinDevOps/33 и https://t.me/javaKotlinDevOps/43. Но кажется, что не хватает примеров. Начнем с антипаттернов - что не нужно делать в тестах?
Есть такая всем известная в Java мире библиотека для создания заглушек как Mockito. И у него есть среди всего прочего такая конструкция как
doNothing().when(mock).someMethod();
На первый взгляд полезная штука - говорим, что при вызове определенного метода в "заглушенном" объекте ничего делать не надо.
Но копнув чуть глубже понимаешь, что результатом вызова
val mock = Mockito.mock(SomeClass.class);
как раз и является объект, который ничего не делает.
А точнее метод объекта не вызывается, а там, где нужно вернуть результат - возвращаются некие значения по умолчанию: https://site.mockito.org/javadoc/current/org/mockito/Mockito.html#2
А если так - явно писать doNothing() - это усложнять код теста, его читаемость и ценность как документации к коду. Держать в уме, что все методы заглушки, которые не переопределены в тесте, не делают ничего - проще и понятнее, чем явно перечислять их.
Теперь уже метод doNothing() выглядит бесполезным. Зачем же его тогда добавляли в библиотеку?
Есть два кейса:
1) когда вы используете spy, а не mock. Напомню, главное отличие spy - он по умолчанию вызывает методы существующего объекта.
Пример:
List list = new LinkedList();
List spy = spy(list);
doNothing().when(spy).clear();
spy.someMethodWithCLeaCall();
2) и об этой возможности я, признаюсь, не знал - в одном выражении можно задать несколько ответов на ряд последовательных вызовов одного метода заглушки:
doNothing()
.doThrow(new RuntimeException())
.when(mock).someVoidMethod();
//первый раз ничего не делаем
mock.someVoidMethod();
//бросаем RuntimeException во второй раз
mock.someVoidMethod();
В следующих постах будет продолжение...
#unittests #mockito #antipatterns
Telegram
(java || kotlin) && devOps
Всем привет!
Каким должен быть хороший тест?
Я в первую очередь про модульные (unit), но в принципе правила применимы к любым.
Основные моменты:
1) правило Arrange, Act, Assert https://xp123.com/articles/3a-arrange-act-assert/
Тест делится на три части:…
Каким должен быть хороший тест?
Я в первую очередь про модульные (unit), но в принципе правила применимы к любым.
Основные моменты:
1) правило Arrange, Act, Assert https://xp123.com/articles/3a-arrange-act-assert/
Тест делится на три части:…
Всем привет!
Продолжим про антипаттерны в unit тестах.
Мы можем проверить в тесте метод на то, что он выбрасывает исключение.
В JUnit 5:
assertThrows(MyException.class, () -> myObject.myFunction(myString), "Expected myFunction() to throw, but it didn't");
и с помощью AssertJ:
assertThatExceptionOfType(MyException.class)
.isThrownBy(() -> { myObject.myFunction(myString); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
Это хорошая проверка, т.к. всегда нужно убедится, что выброшено именно то исключение, которое мы ожидаем. А иногда требуется проверить его параметры.
Но есть еще методы проверки, что исключения не было.
В JUnit 5:
assertDoesNotThrow(() -> myObject.myFunction(myString));
и AssertJ:
assertThatNoException()
.isThrownBy(() -> myObject.myFunction(myString));
А вот эти проверки выглядят излишними, т.к. выбрасывание исключения в любом случае прерывает тест, и он подсвечивается красным.
Возможно, если тест сложный и в нем много проверок - хочется локализовать место ошибки. Но в этом случае я бы задумался про разделение теста на несколько вместо использования assertDoesNotThrow. Опять же, в противном случае придется использовать данную проверку во всех тестах, кроме тех, что выбрасывают исключение. А это IMHO "замусоривает" код.
P.S. Интересный момент - библиотека Truth, о которой я писал ранее https://t.me/javaKotlinDevOps/51, вообще не содержит методов проверки на выбрасывание исключения, предлагая пользоваться соответствующим методом из JUnit, ведь assertThrows, описанный ранее, возвращает исключение:
val exception = assertThrows(MyException.class, () -> myObject.myFunction());
А уже его можно проверить более детально:
assertThat(exception)
.hasCauseThat()
.isInstanceOf(NumberFormatException.class);
assertThat(exception)
.hasMessageThat()
.startsWith("Bad");
Мне этот подход нравится.
#unittests #junit #assertj #truth #antipatterns
Продолжим про антипаттерны в unit тестах.
Мы можем проверить в тесте метод на то, что он выбрасывает исключение.
В JUnit 5:
assertThrows(MyException.class, () -> myObject.myFunction(myString), "Expected myFunction() to throw, but it didn't");
и с помощью AssertJ:
assertThatExceptionOfType(MyException.class)
.isThrownBy(() -> { myObject.myFunction(myString); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();
Это хорошая проверка, т.к. всегда нужно убедится, что выброшено именно то исключение, которое мы ожидаем. А иногда требуется проверить его параметры.
Но есть еще методы проверки, что исключения не было.
В JUnit 5:
assertDoesNotThrow(() -> myObject.myFunction(myString));
и AssertJ:
assertThatNoException()
.isThrownBy(() -> myObject.myFunction(myString));
А вот эти проверки выглядят излишними, т.к. выбрасывание исключения в любом случае прерывает тест, и он подсвечивается красным.
Возможно, если тест сложный и в нем много проверок - хочется локализовать место ошибки. Но в этом случае я бы задумался про разделение теста на несколько вместо использования assertDoesNotThrow. Опять же, в противном случае придется использовать данную проверку во всех тестах, кроме тех, что выбрасывают исключение. А это IMHO "замусоривает" код.
P.S. Интересный момент - библиотека Truth, о которой я писал ранее https://t.me/javaKotlinDevOps/51, вообще не содержит методов проверки на выбрасывание исключения, предлагая пользоваться соответствующим методом из JUnit, ведь assertThrows, описанный ранее, возвращает исключение:
val exception = assertThrows(MyException.class, () -> myObject.myFunction());
А уже его можно проверить более детально:
assertThat(exception)
.hasCauseThat()
.isInstanceOf(NumberFormatException.class);
assertThat(exception)
.hasMessageThat()
.startsWith("Bad");
Мне этот подход нравится.
#unittests #junit #assertj #truth #antipatterns
Telegram
(java || kotlin) && devOps
Всем привет!
Не JUnit-ом единым...
Если говорить о фреймворках для unit тестирования все наверняка вспомнят JUnit и Mockito. Но есть ещё много чего полезного в этой области. Сегодня расскажу про библиотеки для улучшения читаемости проверок - assert. Про…
Не JUnit-ом единым...
Если говорить о фреймворках для unit тестирования все наверняка вспомнят JUnit и Mockito. Но есть ещё много чего полезного в этой области. Сегодня расскажу про библиотеки для улучшения читаемости проверок - assert. Про…
Всем привет!
Еще один антипаттерн в unit тестах - чрезмерное увлечение verify.
List<String> mockedList = mock(MyList.class);
mockedList.size();
verify(mockedList, times(1)).size();
Да, штука полезная. Полезная по большому счету в 2 случаях:
1) проверка тестируемого метода в случае, когда он ничего не возвращает. К слову - еще одна альтернативная проверка: для mutable объекта и меняющего состояние метода проверить поля SUT (System under test).
2) проверка важных шагов алгоритма.
Если с void методом все понятно, то с деталями алгоритма главное - найти ту черту, которая отделяет суть алгоритма от деталей реализации. И проверять только первое. Т.к. в противном случае мы получим "хрупкий" тест, падающий при каждом изменении реализации. Собственно, от этого критерия можно и отталкиваться. Пример: если назначение данного сервисного класса вызов удаленной системы, отбрасывание метрик и логов - то именно эти вызовы и нужно проверить. В других случаях вызов метода логирования и отправку метрики я бы не проверял.
Еще хороший критерий - проверять те вызовы, которые описаны в аналитике.
Ясно, что 100% гарантии того, что тест проживет в неизменном виде долго и счастливо это не даст, мы же не провидцы) Но частота исправлений теста снизится.
Есть еще одна радикальная альтернатива - вообще не тестировать в рамках unit тестов методы, где все, что мы можем проверить - это вызывался тот или иной метод. А оставить это на интеграционные тесты.
#unittests #antipatterns #mockito
Еще один антипаттерн в unit тестах - чрезмерное увлечение verify.
List<String> mockedList = mock(MyList.class);
mockedList.size();
verify(mockedList, times(1)).size();
Да, штука полезная. Полезная по большому счету в 2 случаях:
1) проверка тестируемого метода в случае, когда он ничего не возвращает. К слову - еще одна альтернативная проверка: для mutable объекта и меняющего состояние метода проверить поля SUT (System under test).
2) проверка важных шагов алгоритма.
Если с void методом все понятно, то с деталями алгоритма главное - найти ту черту, которая отделяет суть алгоритма от деталей реализации. И проверять только первое. Т.к. в противном случае мы получим "хрупкий" тест, падающий при каждом изменении реализации. Собственно, от этого критерия можно и отталкиваться. Пример: если назначение данного сервисного класса вызов удаленной системы, отбрасывание метрик и логов - то именно эти вызовы и нужно проверить. В других случаях вызов метода логирования и отправку метрики я бы не проверял.
Еще хороший критерий - проверять те вызовы, которые описаны в аналитике.
Ясно, что 100% гарантии того, что тест проживет в неизменном виде долго и счастливо это не даст, мы же не провидцы) Но частота исправлений теста снизится.
Есть еще одна радикальная альтернатива - вообще не тестировать в рамках unit тестов методы, где все, что мы можем проверить - это вызывался тот или иной метод. А оставить это на интеграционные тесты.
#unittests #antipatterns #mockito
Всем привет!
Еще одна проблема, которую я замечаю в unit тестах - это засилье copy-paste. Тесты есть, покрытие хорошее, тесты проходят. Все хорошо?
Не совсем.
Как я уже писал тест - это самая лучшая документация к коду. Поэтому если в тесте есть mock объект, скопированный с другого теста, но на самом деле не нужный для успешного прохождения теста - это вводит в заблуждение при изучении проекта. Аналогично - если есть лишние проверки verify, которые конкретно у данного теста всегда успешны или не проверяют ничего полезного, зато приводят к хрупкости теста.
Резюме: тесты - это тоже код, к их написанию нужно относится с той же серьезностью, как и к коду.
#unittests #antipatterns
Еще одна проблема, которую я замечаю в unit тестах - это засилье copy-paste. Тесты есть, покрытие хорошее, тесты проходят. Все хорошо?
Не совсем.
Как я уже писал тест - это самая лучшая документация к коду. Поэтому если в тесте есть mock объект, скопированный с другого теста, но на самом деле не нужный для успешного прохождения теста - это вводит в заблуждение при изучении проекта. Аналогично - если есть лишние проверки verify, которые конкретно у данного теста всегда успешны или не проверяют ничего полезного, зато приводят к хрупкости теста.
Резюме: тесты - это тоже код, к их написанию нужно относится с той же серьезностью, как и к коду.
#unittests #antipatterns
Всем привет!
Есть такая отличная штука в Kotlin, как контекстные функции.
Вот документация https://kotlinlang.org/docs/scope-functions.html
Вот пример, хорошо иллюстрирующий зачем они нужны:
val man = Person("Vasya").apply {
age = 20 // same as this.age = 20
city = "Moscow"
}
Код стал проще, читается хорошо. Если, конечно, ты знаешь про контекстные функции)
Но как и любая вещь, контекстные функции могут быть использованы не только во благо(
Вот несколько антипаттернов:
val params = claim.systemState?.let {
FailureParams(
claim.partnerName,
it.name,
)
}
Что мне здесь не нравится - читаемость. Если читать сверху вниз, то получается, что мы берем статус из заявки и присваиваем переменной params ... не его, а совершенно другой объект, созданный внутри let. Скорость понимания кода страдает.
return claim.also {
saveToCache(it)
}
Опять же, мне не нравится читаемость кода. Мы возвращаем результат метода, claim. А нет, не возвращаем. Вначале пишем его в кэш. А потом уже возвращаем.
Гораздо проще было бы:
saveToCache(claim)
return claim
Ну и наконец самый хит:
return claim.also {
saveToCache(someOtherObject)
}
Зачем? Почему? Не понятно)
P.S. По ссылке выше есть неплохая табличка https://kotlinlang.org/docs/scope-functions.html#function-selection Это по сути навигатор по контекстным функциям - позволяет выбрать одну из 6 для вашего конкретного случая. На первое время точно будет полезной
#kotlin #antipatterns
Есть такая отличная штука в Kotlin, как контекстные функции.
Вот документация https://kotlinlang.org/docs/scope-functions.html
Вот пример, хорошо иллюстрирующий зачем они нужны:
val man = Person("Vasya").apply {
age = 20 // same as this.age = 20
city = "Moscow"
}
Код стал проще, читается хорошо. Если, конечно, ты знаешь про контекстные функции)
Но как и любая вещь, контекстные функции могут быть использованы не только во благо(
Вот несколько антипаттернов:
val params = claim.systemState?.let {
FailureParams(
claim.partnerName,
it.name,
)
}
Что мне здесь не нравится - читаемость. Если читать сверху вниз, то получается, что мы берем статус из заявки и присваиваем переменной params ... не его, а совершенно другой объект, созданный внутри let. Скорость понимания кода страдает.
return claim.also {
saveToCache(it)
}
Опять же, мне не нравится читаемость кода. Мы возвращаем результат метода, claim. А нет, не возвращаем. Вначале пишем его в кэш. А потом уже возвращаем.
Гораздо проще было бы:
saveToCache(claim)
return claim
Ну и наконец самый хит:
return claim.also {
saveToCache(someOtherObject)
}
Зачем? Почему? Не понятно)
P.S. По ссылке выше есть неплохая табличка https://kotlinlang.org/docs/scope-functions.html#function-selection Это по сути навигатор по контекстным функциям - позволяет выбрать одну из 6 для вашего конкретного случая. На первое время точно будет полезной
#kotlin #antipatterns
Kotlin Help
Scope functions | Kotlin
Всем привет!
Сегодня пост о крутой фиче Kotlin, которая решила одну важную проблему. И добавила другую)
Я о Null safety.
Суть ее в том, что в Kotlin любой тип представлен в двух ипостасях - одна может содержать null значения, другая - нет.
String - не может содержать null,
String? - может.
Это два разных типа, неявное приведение второго к первому "без приседаний" невозможно.
Что дает Null safety?
По умолчанию предлагается использовать тип not null, и если так и делать, то кажется, что про NPE - NullPointerException - можно забыть. А заодно забыть о проверках на null как в коде, так и в тестах. Небольшой оффтоп - проверка not null значений на null в тестах - еще один антипаттерн. Говорит о том, что пишущий этот код еще не до конца познал Kotlin)
Вроде бы все хорошо. Но есть нюанс. Что будет, если присвоить not null переменной nullable значение? Ошибка компиляции. А всегда ли компилятор знает, что это nullable значение? Если тип Kotlin - то всегда. Если тип Java - то в общем случае не знает. Что же он делает в таком случае? А ничего, просто разрешает присваивание.
//Java
public class JavaNullability {
private Boolean value;
public Boolean getValue() {
return value;
}
}
// Kotlin
class KotlinNullability {
constructor(test: JavaNullability) {
val nullValue: Boolean? = test.value
val notNullValue: Boolean = test.value
// компилятор разрешает оба варианта
}
}
Что же будет в runtime? Привычный нам в Java NPE на втором присваивании. Да, null safety не защищает от NPE в Kotlin коде.
Что тут можно сделать?
1) при взаимодействии Kotlin-Java знать nullability Java параметров, с которым мы работаем. Это может быть описание контракта - OpenAPI спецификация или любой другой контракт.
2) явная проверка на null в коде присваивания
3) самый плохой вариант, антипаттерн - всегда присваивать значения из Java nullable типам в Kotlin. Почему это плохо? nullable типы расползаются по всей программе, и в итоге мы теряем все преимущества Kotlin null safety.
P.S. Но в целом: null safety - это круто! Подробнее можно о ней можно почитать в официальной документации: https://kotlinlang.org/docs/null-safety.html#nullable-receiver
#kotlin #java #antipatterns #nullsafety
Сегодня пост о крутой фиче Kotlin, которая решила одну важную проблему. И добавила другую)
Я о Null safety.
Суть ее в том, что в Kotlin любой тип представлен в двух ипостасях - одна может содержать null значения, другая - нет.
String - не может содержать null,
String? - может.
Это два разных типа, неявное приведение второго к первому "без приседаний" невозможно.
Что дает Null safety?
По умолчанию предлагается использовать тип not null, и если так и делать, то кажется, что про NPE - NullPointerException - можно забыть. А заодно забыть о проверках на null как в коде, так и в тестах. Небольшой оффтоп - проверка not null значений на null в тестах - еще один антипаттерн. Говорит о том, что пишущий этот код еще не до конца познал Kotlin)
Вроде бы все хорошо. Но есть нюанс. Что будет, если присвоить not null переменной nullable значение? Ошибка компиляции. А всегда ли компилятор знает, что это nullable значение? Если тип Kotlin - то всегда. Если тип Java - то в общем случае не знает. Что же он делает в таком случае? А ничего, просто разрешает присваивание.
//Java
public class JavaNullability {
private Boolean value;
public Boolean getValue() {
return value;
}
}
// Kotlin
class KotlinNullability {
constructor(test: JavaNullability) {
val nullValue: Boolean? = test.value
val notNullValue: Boolean = test.value
// компилятор разрешает оба варианта
}
}
Что же будет в runtime? Привычный нам в Java NPE на втором присваивании. Да, null safety не защищает от NPE в Kotlin коде.
Что тут можно сделать?
1) при взаимодействии Kotlin-Java знать nullability Java параметров, с которым мы работаем. Это может быть описание контракта - OpenAPI спецификация или любой другой контракт.
2) явная проверка на null в коде присваивания
3) самый плохой вариант, антипаттерн - всегда присваивать значения из Java nullable типам в Kotlin. Почему это плохо? nullable типы расползаются по всей программе, и в итоге мы теряем все преимущества Kotlin null safety.
P.S. Но в целом: null safety - это круто! Подробнее можно о ней можно почитать в официальной документации: https://kotlinlang.org/docs/null-safety.html#nullable-receiver
#kotlin #java #antipatterns #nullsafety
Kotlin Help
Null safety | Kotlin
Всем привет!
Чтобы проиллюстрировать предыдущий пост - предлагаю сравнить по критерию null safety три языка: Groovy, Java и Kotlin.
Тестовая задача такая - передать null Boolean значение в конструктор и проверить его в if.
Groovy
import groovy.transform.TupleConstructor
@TupleConstructor
class GroovyNullability {
Boolean value
void checkValue() {
if (!value) {
println 'groovy value is false'
}
}
}
Т.к. в Groovy все, что не true считается false - вызов checkValue распечатает строку.
Java
public class JavaNullability {
private Boolean value;
public JavaNullability(Boolean value) {
this.value = value;
}
public void checkValue() {
if (!value) {
System.out.println("java value is false");
}
}
}
Вызов checkValue упадет c NPE: "Cannot invoke "java.lang.Boolean.booleanValue()" because "this.value" is null" на преобразовании Boolean к boolean использования в логическом выражении (boolean expression).
Kotlin
class KotlinNullability {
var value: Boolean = false
constructor(test: Boolean?) {
value = test!!
}
fun checkValue() {
if (!value) {
println("kotlin value is false")
}
}
}
Чтобы присвоить null значение not null переменной придется пойти на хитрость - через !! указать, что мы уверены, что в nullable переменной не будет null значений. И если же null все же придет - тоже будет java.lang.NullPointerException, причем без подробностей. Но на шаг раньше, еще на присваивании not null переменной.
К слову - если передать Java объект с null значением, через класс-обвертку:
constructor(test: JavaNullability) {
value = test.value
}
NPE будет чуть более подробным: "java.lang.NullPointerException: test.value must not be null"
#kotlin #java #groovy #nullsafety
Чтобы проиллюстрировать предыдущий пост - предлагаю сравнить по критерию null safety три языка: Groovy, Java и Kotlin.
Тестовая задача такая - передать null Boolean значение в конструктор и проверить его в if.
Groovy
import groovy.transform.TupleConstructor
@TupleConstructor
class GroovyNullability {
Boolean value
void checkValue() {
if (!value) {
println 'groovy value is false'
}
}
}
Т.к. в Groovy все, что не true считается false - вызов checkValue распечатает строку.
Java
public class JavaNullability {
private Boolean value;
public JavaNullability(Boolean value) {
this.value = value;
}
public void checkValue() {
if (!value) {
System.out.println("java value is false");
}
}
}
Вызов checkValue упадет c NPE: "Cannot invoke "java.lang.Boolean.booleanValue()" because "this.value" is null" на преобразовании Boolean к boolean использования в логическом выражении (boolean expression).
Kotlin
class KotlinNullability {
var value: Boolean = false
constructor(test: Boolean?) {
value = test!!
}
fun checkValue() {
if (!value) {
println("kotlin value is false")
}
}
}
Чтобы присвоить null значение not null переменной придется пойти на хитрость - через !! указать, что мы уверены, что в nullable переменной не будет null значений. И если же null все же придет - тоже будет java.lang.NullPointerException, причем без подробностей. Но на шаг раньше, еще на присваивании not null переменной.
К слову - если передать Java объект с null значением, через класс-обвертку:
constructor(test: JavaNullability) {
value = test.value
}
NPE будет чуть более подробным: "java.lang.NullPointerException: test.value must not be null"
#kotlin #java #groovy #nullsafety
Резюмирую предыдущий пост:
1) в Groovy нет null safety
2) в Java проблемы с null проявятся при попытке работы с null объектом, в т.ч. при неявных преобразованиях
3) в Kotlin с использованием Java библиотек проблемы будут проявляться при присваивании в Kotlin коде
4) чистый Kotlin = null safety
#groovy #kotlin #java #nullsafety
1) в Groovy нет null safety
2) в Java проблемы с null проявятся при попытке работы с null объектом, в т.ч. при неявных преобразованиях
3) в Kotlin с использованием Java библиотек проблемы будут проявляться при присваивании в Kotlin коде
4) чистый Kotlin = null safety
#groovy #kotlin #java #nullsafety
Всем привет!
Если посмотреть на тему null safety с другой стороны - то это лишь один из аспектов более широкого принципа. Я бы сформулировал его как: "Знай свои данные".
Что же нужно знать:
1) кодировку текстовых данных - всегда явно ее указывать, не ждать, что всегда будет UTF-8
2) часовой пояс\locale - опять же не надеяться на то, что CI pipeline, тестовые и ПРОМ стенды работают в одном часовом поясе. А даже если это так - что на всех правильно настроен часовой пояс) И что никто не запусти код в другом часовом поясе. И никто не пришлет время из другого часового пояса
3) файловую систему, в частности разделители в пути к файлу. Для них есть специальные константы в Java Core, ОС в которой происходит запуск также можно определить
4) все проверки на injection - sql injection и аналоги
5) указание конкретных проверяемых исключений вместо Exception. Это не совсем данные, но часто исключения вызываются некорректными данными
6) unit тесты на граничные значения
7) ну и наконец null safety - может ли по бизнес-процессу значение принимать null или нет
Принцип можно расширить до инфраструктуры и добавить к примеру версии используемых библиотек и плагинов. Если версии не фиксировать - могут подтянутся новые в неожиданный момент с неожиданным поведением)
P.S. Если что-то забыл - дополняйте в комментах
#data #principles
Если посмотреть на тему null safety с другой стороны - то это лишь один из аспектов более широкого принципа. Я бы сформулировал его как: "Знай свои данные".
Что же нужно знать:
1) кодировку текстовых данных - всегда явно ее указывать, не ждать, что всегда будет UTF-8
2) часовой пояс\locale - опять же не надеяться на то, что CI pipeline, тестовые и ПРОМ стенды работают в одном часовом поясе. А даже если это так - что на всех правильно настроен часовой пояс) И что никто не запусти код в другом часовом поясе. И никто не пришлет время из другого часового пояса
3) файловую систему, в частности разделители в пути к файлу. Для них есть специальные константы в Java Core, ОС в которой происходит запуск также можно определить
4) все проверки на injection - sql injection и аналоги
5) указание конкретных проверяемых исключений вместо Exception. Это не совсем данные, но часто исключения вызываются некорректными данными
6) unit тесты на граничные значения
7) ну и наконец null safety - может ли по бизнес-процессу значение принимать null или нет
Принцип можно расширить до инфраструктуры и добавить к примеру версии используемых библиотек и плагинов. Если версии не фиксировать - могут подтянутся новые в неожиданный момент с неожиданным поведением)
P.S. Если что-то забыл - дополняйте в комментах
#data #principles
Всем привет!
Постов долгое время не было, причина типичная - много работы. Вообще не помню времени, когда ее было мало((( И были ли вообще такие времена?)
Хотел бы поднять сегодня такую важную тему как взаимодействие разработчиков и сопровождения.
Для начала одна общеизвестная информация - разработчики и сопровождение исходя из своих задач обречены на противостояние.
Задача разработчиков - менять приложение, задача сопровождения - обеспечивать его работоспособность. А как известно: работает - не трогай) Любое изменение потенциальный источник проблем.
Отсюда часто следует одна крайность - сопровождение максимально критично относится к любым изменениям, разработка "виновна" по умолчанию, требуется строгое соблюдение регламентов, любая нестандартная просьба разработки встречается "в штыки".
Почему это плохо?
Сейчас основная методология разработки - это Agile в разных вариациях. Один из ключевых моментов в Agile - это команда, командная ответственность, гибкие решения в команде. А сопровождение в описываемом кейсе выступает внешним "врагом" - блокирует инициативы команды, замедляет скорость выпуска новых версий. А высокая частота выхода в ПРОМ - еще одна важная часть Agile.
С таким кейсом я, увы, сталкивался и наблюдал его губительный для команды эффект. Часто свои требования сопровождение объясняет требованиями надежности. Хорошее ли это объяснение - зависит от деталей. Если объяснение звучит как-то так - это снизит надежность, т.к. ... и идет описание причин - то да, хорошее.
Если слово надежность произносится, а никаких деталей не приводится - это признак того, что сопровождение боится изменений и не хочет развиваться.
Есть и другая крайность - сопровождение "согласно на все") Не выставляет никаких требований, принимает любые дистрибутивы. Кейс более редкий. Чем это плохо - разработчики опять же исходя из своих основных задач редко думают о том, как их код будет сопровождаться. Обычно на разработку времени хватает впритык.
Какой выход из данной ситуации?
Выставлять требования со стороны сопровождения.
Требования зависят от компании, отрасли, приложения, числа пользователей и много чего.
Но базовые требования могут быть такими:
1) список метрик, позволяющих отслеживать работоспособность
2) требования к логам - где и в каком объеме. Сюда же я бы добавил требования к фильтрации логов, с важным дополнением - возможность фильтрации зависит как от разработчиков бизнес-приложения, так и от разработчиков системы просмотра логов
3) требования к трассировке (tracing) - особенно важно если мы имеем дело с микросервисами
4) наличие инструкции для сопровождения в случае, если установка релиза требует ручных действий
5) наличие сценария отката на предыдущую версию. Это может быть выключение feature toggle или номер версии для отката. Самое важное - сама возможность отката. Это тоже требование, его нужно или соблюдать, или если это невозможно, например, в случае необратимых изменений в БД - составлять план действий на случай неудачной раскатки
6) фиксирование таймаутов внешних вызовов: я уже писал что бесконечные таймауты - одна из основных причин падения приложения
7) требования по UI для ручного разбора ошибок, если все предыдущие требования не помогли)
Почему я говорю "могут быть такие требования" - я все же изначально разработчик, поэтому имею некие предустановки и не вижу полной картины. Если есть возражения и дополнения - возражайте и дополняйте)
#dev #ops
Постов долгое время не было, причина типичная - много работы. Вообще не помню времени, когда ее было мало((( И были ли вообще такие времена?)
Хотел бы поднять сегодня такую важную тему как взаимодействие разработчиков и сопровождения.
Для начала одна общеизвестная информация - разработчики и сопровождение исходя из своих задач обречены на противостояние.
Задача разработчиков - менять приложение, задача сопровождения - обеспечивать его работоспособность. А как известно: работает - не трогай) Любое изменение потенциальный источник проблем.
Отсюда часто следует одна крайность - сопровождение максимально критично относится к любым изменениям, разработка "виновна" по умолчанию, требуется строгое соблюдение регламентов, любая нестандартная просьба разработки встречается "в штыки".
Почему это плохо?
Сейчас основная методология разработки - это Agile в разных вариациях. Один из ключевых моментов в Agile - это команда, командная ответственность, гибкие решения в команде. А сопровождение в описываемом кейсе выступает внешним "врагом" - блокирует инициативы команды, замедляет скорость выпуска новых версий. А высокая частота выхода в ПРОМ - еще одна важная часть Agile.
С таким кейсом я, увы, сталкивался и наблюдал его губительный для команды эффект. Часто свои требования сопровождение объясняет требованиями надежности. Хорошее ли это объяснение - зависит от деталей. Если объяснение звучит как-то так - это снизит надежность, т.к. ... и идет описание причин - то да, хорошее.
Если слово надежность произносится, а никаких деталей не приводится - это признак того, что сопровождение боится изменений и не хочет развиваться.
Есть и другая крайность - сопровождение "согласно на все") Не выставляет никаких требований, принимает любые дистрибутивы. Кейс более редкий. Чем это плохо - разработчики опять же исходя из своих основных задач редко думают о том, как их код будет сопровождаться. Обычно на разработку времени хватает впритык.
Какой выход из данной ситуации?
Выставлять требования со стороны сопровождения.
Требования зависят от компании, отрасли, приложения, числа пользователей и много чего.
Но базовые требования могут быть такими:
1) список метрик, позволяющих отслеживать работоспособность
2) требования к логам - где и в каком объеме. Сюда же я бы добавил требования к фильтрации логов, с важным дополнением - возможность фильтрации зависит как от разработчиков бизнес-приложения, так и от разработчиков системы просмотра логов
3) требования к трассировке (tracing) - особенно важно если мы имеем дело с микросервисами
4) наличие инструкции для сопровождения в случае, если установка релиза требует ручных действий
5) наличие сценария отката на предыдущую версию. Это может быть выключение feature toggle или номер версии для отката. Самое важное - сама возможность отката. Это тоже требование, его нужно или соблюдать, или если это невозможно, например, в случае необратимых изменений в БД - составлять план действий на случай неудачной раскатки
6) фиксирование таймаутов внешних вызовов: я уже писал что бесконечные таймауты - одна из основных причин падения приложения
7) требования по UI для ручного разбора ошибок, если все предыдущие требования не помогли)
Почему я говорю "могут быть такие требования" - я все же изначально разработчик, поэтому имею некие предустановки и не вижу полной картины. Если есть возражения и дополнения - возражайте и дополняйте)
#dev #ops
Всем привет!
На собеседовании я иногда задаю вопрос: приведите пример нарушения принципа Single responsibility. Или альтернативный вариант - а вот если в методе, к примеру, activateCard мы заодно отбросим метрики или залогируем результат - это нарушение принципа или нет.
На первый взгляд ответ - нет. Метрики и логи - это технический код, не бизнес функционал. Он может понадобиться в любом месте кода. Часто такой функционал реализуют с помощью аспектов, т.к. во-первых - это можно реализовать с помощью аспектов, а во-вторых - это красиво))), т.е. некий синтаксический сахар, улучшающий читаемость кода.
Но можно рассмотреть немного другую ситуацию. Предположим, есть код с математическими вычислениям. Или любой алгоритм. Или логика обработки данных. То, что хорошо реализуется в функциональном стиле - входные данные метода, результат, никаких внешних зависимостей. В нём нет внешних взаимодействий, сохранения в хранилище. Чистая логика. В этом случае логирование и метрики - это уже некая обработка полученного результата. Мы же не просто так выводим что-то в лог - это либо данные для разбора ошибки, либо отслеживание пользовательского пути, сбор статистики, отслеживание времени выполнения кода... Т.е. есть отдельная логика по месту и составу того, что мы логируем. Опять же контекст логирования часто требует инициализации, что добавляет ненужные зависимости в нашу логику. Поэтому такой код лучше поместить на уровень выше.
Итого: бизнес функционал и логирование/метрики - да, "чистая" логика - нет.
#logging #metrics #interview_question #code_design #solid #dev_compromises
На собеседовании я иногда задаю вопрос: приведите пример нарушения принципа Single responsibility. Или альтернативный вариант - а вот если в методе, к примеру, activateCard мы заодно отбросим метрики или залогируем результат - это нарушение принципа или нет.
На первый взгляд ответ - нет. Метрики и логи - это технический код, не бизнес функционал. Он может понадобиться в любом месте кода. Часто такой функционал реализуют с помощью аспектов, т.к. во-первых - это можно реализовать с помощью аспектов, а во-вторых - это красиво))), т.е. некий синтаксический сахар, улучшающий читаемость кода.
Но можно рассмотреть немного другую ситуацию. Предположим, есть код с математическими вычислениям. Или любой алгоритм. Или логика обработки данных. То, что хорошо реализуется в функциональном стиле - входные данные метода, результат, никаких внешних зависимостей. В нём нет внешних взаимодействий, сохранения в хранилище. Чистая логика. В этом случае логирование и метрики - это уже некая обработка полученного результата. Мы же не просто так выводим что-то в лог - это либо данные для разбора ошибки, либо отслеживание пользовательского пути, сбор статистики, отслеживание времени выполнения кода... Т.е. есть отдельная логика по месту и составу того, что мы логируем. Опять же контекст логирования часто требует инициализации, что добавляет ненужные зависимости в нашу логику. Поэтому такой код лучше поместить на уровень выше.
Итого: бизнес функционал и логирование/метрики - да, "чистая" логика - нет.
#logging #metrics #interview_question #code_design #solid #dev_compromises
Всем привет!
Может ли API с широким функционалом стать проблемой? Имея несколько реализаций от разных производителей ПО, являясь эталонной реализацией паттернов интеграции https://www.enterpriseintegrationpatterns.com/ при этом быть хуже конкретного продукта с ограниченым набором функционала?
Как можно догадаться из вопроса - ответ: да. Я про JMS vs Kafka.
JMS - это API из состава Java EE (сейчас Jakarta EE). Есть несколько реализаций: практически у каждого сервера приложений есть свой JMS - IBM, Oracle, JBoss, SAP, есть и Open source решения - ActiveMQ, Artemis MQ и другие.
Что есть в JMS? Стандарт широкий: есть очереди (точка-точка, ака P2P) и топики (подписка, ака PubSub), опциональная персистентность и транзационность, возможность настраиваемой маршрутизации и конвертации сообщений. И с security все хорошо.
У Kafka же только топики, нет продвинутой маршрутизации, трансформации, персистентность постоянная, транзакционности нету, гарантии однократной вычитки должен обеспечивать клиент. Да и вендор один. Но справедливости ради vendor lock нет, т.к продукт open source, за деньги только поддержка.
При этом Kafka успешно отвоевывает долю рынка. В чем же дело?
Секрет Kafka в том, что она выпустила достаточно простой, очень быстрый open source продукт, достаточный для большого числа клиентов. Этим нивелируется преимущество JMS в возможности выбора реализации. Причем Kafka быстрая с включённой по умолчанию персистентностью, а значит и высокой надёжностью. По скорости однозначно бьет все реализации JMS. Про то, как удалось добиться такого результата, я писал тут https://t.me/javaKotlinDevOps/91
В чем проблемы JMS:
попытка объять необъятное в API = переусложнение,
много вендорской специфики, которая может помешать смену реализации,
слишком большая роль брокера - все возможности по маршрутизации и трансформации не бесплатны по производительности, и кроме того ведут к тому, что обычная очередь превращается в Enterprise Service Bus, а у этой концепции есть свои минусы.
Я не хочу сказать, что JMS можно выкидывать, а лишь пишу почему Kafka удалось ее так сильно потеснить. Если вам нужно взаимодействие точка-точка и нет больших объёмов данных - JMS вполне подойдёт.
#kafka #jms #comparison
Может ли API с широким функционалом стать проблемой? Имея несколько реализаций от разных производителей ПО, являясь эталонной реализацией паттернов интеграции https://www.enterpriseintegrationpatterns.com/ при этом быть хуже конкретного продукта с ограниченым набором функционала?
Как можно догадаться из вопроса - ответ: да. Я про JMS vs Kafka.
JMS - это API из состава Java EE (сейчас Jakarta EE). Есть несколько реализаций: практически у каждого сервера приложений есть свой JMS - IBM, Oracle, JBoss, SAP, есть и Open source решения - ActiveMQ, Artemis MQ и другие.
Что есть в JMS? Стандарт широкий: есть очереди (точка-точка, ака P2P) и топики (подписка, ака PubSub), опциональная персистентность и транзационность, возможность настраиваемой маршрутизации и конвертации сообщений. И с security все хорошо.
У Kafka же только топики, нет продвинутой маршрутизации, трансформации, персистентность постоянная, транзакционности нету, гарантии однократной вычитки должен обеспечивать клиент. Да и вендор один. Но справедливости ради vendor lock нет, т.к продукт open source, за деньги только поддержка.
При этом Kafka успешно отвоевывает долю рынка. В чем же дело?
Секрет Kafka в том, что она выпустила достаточно простой, очень быстрый open source продукт, достаточный для большого числа клиентов. Этим нивелируется преимущество JMS в возможности выбора реализации. Причем Kafka быстрая с включённой по умолчанию персистентностью, а значит и высокой надёжностью. По скорости однозначно бьет все реализации JMS. Про то, как удалось добиться такого результата, я писал тут https://t.me/javaKotlinDevOps/91
В чем проблемы JMS:
попытка объять необъятное в API = переусложнение,
много вендорской специфики, которая может помешать смену реализации,
слишком большая роль брокера - все возможности по маршрутизации и трансформации не бесплатны по производительности, и кроме того ведут к тому, что обычная очередь превращается в Enterprise Service Bus, а у этой концепции есть свои минусы.
Я не хочу сказать, что JMS можно выкидывать, а лишь пишу почему Kafka удалось ее так сильно потеснить. Если вам нужно взаимодействие точка-точка и нет больших объёмов данных - JMS вполне подойдёт.
#kafka #jms #comparison
Enterprise Integration Patterns
A comprehensive pattern language for the robust design of asynchronous messaging solutions. The patterns stay product neutral and emphasize design trade-offs over specific technology choices.
Всем привет!
Я уже писал о важности рефакторинга.
Но как оценить, что рефакторинг достиг своих целей?
Можно экспертно. Но не всегда этот вариант годится для обоснования времени на рефакторинг у бизнеса.
Можно ли как-то подтвердить эффект цифрами?
Самый простой вариант - если проблема в производительности. Тогда изменяем TPS - transaction per second - до и после и демонстрируем эффект.
А если проблема в сложности кода как это чаще всего бывает?
Тогда на помощь могут прийти следующие метрики:
- цикломатическая сложность кода и другие похожие метрики https://www.tiobe.com/knowledge/article/demystifying-code-complexity/ После рефакторинга сложность должна снизиться, при этом в процессе может временно повышаться. А стандартные метрики хороши тем, что уже есть утилиты для их подсчёта. Например, Checkstyle https://checkstyle.org/checks/metrics/cyclomaticcomplexity.html или Sonarqube https://habr.com/ru/articles/565652/ Также благодаря тому, что эти метрики широко известны в узких кругах разработчиков )) их проще объяснить бизнесу. Или пусть изучают формулу, или верят на слово разработчикам)
- если сложность кода по каким-то причинам не подходит - можно рассмотреть среднее число строк в классе или методе. После рефакторинга это число также должно снижаться,
- в некоторых случаях может даже подойти абсолютное число строк кода. Если приложение «раздуто», то результатом рефакторинга станет уменьшение размера кодовой базы.
- число TODO в коде. Каждая TODO - потенциальный техдолг.
- число длинных комментариев в коде. Если код самодокументирующийся - с говорящими названиями методов и классов - то длинный комментарий можно рассматривать как аналог TODO
- скорость вникания в код сервиса для новых разработчиков. Изменить достаточно трудоёмко, но метрика хорошая
- число ошибок ПРОМ, связанных с изменяемым кодом. Если их много - значит с кодом что-то не так
- постоянно увеличивающееся или стабильно большое время разработки новых фичей. Например, по сравнению с временем в начале жизненного цикла сервиса. Сравнивать с другими командами и сервисам часто не корректно.
Отдельно я бы рассмотрел случай, когда новая фича не вписывается в существующую архитектуру. Может получится, что просто сделать ее будет быстрее, чем отрефакторить и сделать без техдолга. Эффект может проявится на следующих фичах, но априори это оценить сложно
#java refactoring #metrics
Я уже писал о важности рефакторинга.
Но как оценить, что рефакторинг достиг своих целей?
Можно экспертно. Но не всегда этот вариант годится для обоснования времени на рефакторинг у бизнеса.
Можно ли как-то подтвердить эффект цифрами?
Самый простой вариант - если проблема в производительности. Тогда изменяем TPS - transaction per second - до и после и демонстрируем эффект.
А если проблема в сложности кода как это чаще всего бывает?
Тогда на помощь могут прийти следующие метрики:
- цикломатическая сложность кода и другие похожие метрики https://www.tiobe.com/knowledge/article/demystifying-code-complexity/ После рефакторинга сложность должна снизиться, при этом в процессе может временно повышаться. А стандартные метрики хороши тем, что уже есть утилиты для их подсчёта. Например, Checkstyle https://checkstyle.org/checks/metrics/cyclomaticcomplexity.html или Sonarqube https://habr.com/ru/articles/565652/ Также благодаря тому, что эти метрики широко известны в узких кругах разработчиков )) их проще объяснить бизнесу. Или пусть изучают формулу, или верят на слово разработчикам)
- если сложность кода по каким-то причинам не подходит - можно рассмотреть среднее число строк в классе или методе. После рефакторинга это число также должно снижаться,
- в некоторых случаях может даже подойти абсолютное число строк кода. Если приложение «раздуто», то результатом рефакторинга станет уменьшение размера кодовой базы.
- число TODO в коде. Каждая TODO - потенциальный техдолг.
- число длинных комментариев в коде. Если код самодокументирующийся - с говорящими названиями методов и классов - то длинный комментарий можно рассматривать как аналог TODO
- скорость вникания в код сервиса для новых разработчиков. Изменить достаточно трудоёмко, но метрика хорошая
- число ошибок ПРОМ, связанных с изменяемым кодом. Если их много - значит с кодом что-то не так
- постоянно увеличивающееся или стабильно большое время разработки новых фичей. Например, по сравнению с временем в начале жизненного цикла сервиса. Сравнивать с другими командами и сервисам часто не корректно.
Отдельно я бы рассмотрел случай, когда новая фича не вписывается в существующую архитектуру. Может получится, что просто сделать ее будет быстрее, чем отрефакторить и сделать без техдолга. Эффект может проявится на следующих фичах, но априори это оценить сложно
#java refactoring #metrics
Всем привет!
Продолжая тему рефакторинга. Основное предусловие для начала рефакторинга - это наличие хорошего тестового покрытия. Т.к. мы не меняем бизнес функционал, а улучшаем код либо для повышения производительности, либо для его упрощения. Но при этом нужно гарантировать, что бизнес функционал не сломался, т.е. не появились регрессионные баги. Ведь рефакторинг, в отличие от новой фичи, может затронуть все приложение. Соответственно, баги могут появиться в любом месте, и без тестового покрытия - это большие риски.
Можно рассмотреть похожий кейс. У нас есть монолит, мы хотим распилить его на микросервисы. Это тоже своего рода рефакторинг, только на уровне архитектурном уровне. И тоже аналогичное условие для его начала - наличие достаточного набора тестов. В данном случае повышается важность интеграционных тестов.
Важный момент: в процессе подготовки к разбиению монолита или серьёзному рефакторингу может возникнуть вопрос-предложение - а давайте все выкинем и напишем заново. Так вот - одним из базовых критериев для ответа на этот вопрос также является покрытие тестами. Очевидно, не единственным, но важным. Другие критерии - объем техдолга, соответствие текущей архитектуры и целевой.
Еще кейс - разработчик «боится» рефакторить код, т.к. он слишком сложный или затрагивает слишком много зависимостей. С тестами решиться на рефакторинг намного проще.
Вывод простой - пишите тесты, это страховка при рефакторинге)
#refactoring #unittests #microservices
Продолжая тему рефакторинга. Основное предусловие для начала рефакторинга - это наличие хорошего тестового покрытия. Т.к. мы не меняем бизнес функционал, а улучшаем код либо для повышения производительности, либо для его упрощения. Но при этом нужно гарантировать, что бизнес функционал не сломался, т.е. не появились регрессионные баги. Ведь рефакторинг, в отличие от новой фичи, может затронуть все приложение. Соответственно, баги могут появиться в любом месте, и без тестового покрытия - это большие риски.
Можно рассмотреть похожий кейс. У нас есть монолит, мы хотим распилить его на микросервисы. Это тоже своего рода рефакторинг, только на уровне архитектурном уровне. И тоже аналогичное условие для его начала - наличие достаточного набора тестов. В данном случае повышается важность интеграционных тестов.
Важный момент: в процессе подготовки к разбиению монолита или серьёзному рефакторингу может возникнуть вопрос-предложение - а давайте все выкинем и напишем заново. Так вот - одним из базовых критериев для ответа на этот вопрос также является покрытие тестами. Очевидно, не единственным, но важным. Другие критерии - объем техдолга, соответствие текущей архитектуры и целевой.
Еще кейс - разработчик «боится» рефакторить код, т.к. он слишком сложный или затрагивает слишком много зависимостей. С тестами решиться на рефакторинг намного проще.
Вывод простой - пишите тесты, это страховка при рефакторинге)
#refactoring #unittests #microservices
Всем привет!
Я упоминал в посте про JMS vs Kafka про ESB - Enterprise Service Bus. Она же Корпоративная Сервисная Шина.
Какие плюсы и минусы данного решения?
Плюсов я вижу три:
1) унификация API в пределах компании
2) единая точка мониторинга и контроля всех интеграций
3) больше возможностей для переиспользования уже существующих API
Минусы такие:
1) т.к. ESB - это отдельная система, то ее разрабатывает как правило отдельная команда, которая быстро становится узким местом. Особенно при внедрении микросервисной архитектуры и резком увеличении числа интеграций
2) ESB как правило вносит дополнительные задержки, что особенно критично при синхронном взаимодействии
3) как правило ESB предлагают коммерческие компании, что приводит к vendor lock. Слезть с такого решения будет сложно
4) неочевидная штука - с одной стороны команда ESB унифицирует все API. Но с другой - API становятся перегруженными. Там будут какие-то стандартизированные для всех поля, общие базовые типы, которые во многих случаях будут избыточными
5) также ESB из-за стандартизации затрудняет развитие API. Т.к. унификация, а кроме того больше команд участвует в согласовании - три вместо двух
Вывод: на данном этапе развития ПО идея ESB выглядит избыточной.
Но что же с плюсами, как добиться того же результата с API точка-точка и микросервисной архитектурой?
1) унификация вещь полезная, главное чтобы она не была избыточной. Архитектор может выставить требования по API, DevOps - встроить их проверку в pipeline.
2) единая точка контроля - в случае облачной среды такой точкой может быть Istio или k8s. Либо ставить proxy на границах сред.
3) для переиспользования можно использовать каталог API. Да и не всегда переиспользование полезно, см. выше про избыточное API. Также отдельное API для каждого потребителя позволяет лучше контролировать доступ к данным
#api #esb
Я упоминал в посте про JMS vs Kafka про ESB - Enterprise Service Bus. Она же Корпоративная Сервисная Шина.
Какие плюсы и минусы данного решения?
Плюсов я вижу три:
1) унификация API в пределах компании
2) единая точка мониторинга и контроля всех интеграций
3) больше возможностей для переиспользования уже существующих API
Минусы такие:
1) т.к. ESB - это отдельная система, то ее разрабатывает как правило отдельная команда, которая быстро становится узким местом. Особенно при внедрении микросервисной архитектуры и резком увеличении числа интеграций
2) ESB как правило вносит дополнительные задержки, что особенно критично при синхронном взаимодействии
3) как правило ESB предлагают коммерческие компании, что приводит к vendor lock. Слезть с такого решения будет сложно
4) неочевидная штука - с одной стороны команда ESB унифицирует все API. Но с другой - API становятся перегруженными. Там будут какие-то стандартизированные для всех поля, общие базовые типы, которые во многих случаях будут избыточными
5) также ESB из-за стандартизации затрудняет развитие API. Т.к. унификация, а кроме того больше команд участвует в согласовании - три вместо двух
Вывод: на данном этапе развития ПО идея ESB выглядит избыточной.
Но что же с плюсами, как добиться того же результата с API точка-точка и микросервисной архитектурой?
1) унификация вещь полезная, главное чтобы она не была избыточной. Архитектор может выставить требования по API, DevOps - встроить их проверку в pipeline.
2) единая точка контроля - в случае облачной среды такой точкой может быть Istio или k8s. Либо ставить proxy на границах сред.
3) для переиспользования можно использовать каталог API. Да и не всегда переиспользование полезно, см. выше про избыточное API. Также отдельное API для каждого потребителя позволяет лучше контролировать доступ к данным
#api #esb
Всем привет!
Рекомендую далеко не новую, но интересную статья от всем известного Тагира Валеева про редкие фичи Java https://habr.com/ru/articles/253787/
#java
Рекомендую далеко не новую, но интересную статья от всем известного Тагира Валеева про редкие фичи Java https://habr.com/ru/articles/253787/
#java
Хабр
10 вещей, которых вы не знали о Java
Итак, вы работаете на Java с самого её появления? Вы помните те дни, когда она называлась «Oak», когда про ООП говорили на каждом углу, когда сиплюсплюсники думали, что у Java нет шансов, а апплеты...
Всем привет!
Ситуация: новый разработчик приходит в команды, начинает разбираться с кодом, pipeline, архитектурой, у него возникают вопросы. Когда стоит подойти к более опытному товарищу - тимлиду, техлиду, senior-у - а в каких случаях разобраться самому?
Однозначного ответа нет. Но есть ряд условий.
Когда стоит разобраться самому?
1) проблема не относится к специфике компании. Т.е. это что-то, что скорее всего можно нагуглить на stackoverflow или хабре. По своему опыту скажу, что такие вопросы очень раздражают, уметь гуглить должны все) Далее я рассматриваю случаи, когда вопрос касается специфики компании. Люди, которые спрашивают у коллег: объясни на пальцах как устроены сервлеты - могут стать героями локальных мемов (из собственного опыта)
2) примерно понятно где искать ответ. Если искать лень - для этого существуют чаты разработчиков. Там отвечает либо те, у кого в данный момент есть свободное время, либо те, кто настроен на помощь коллегам. А возможно даже "специально обученные" для ответов на вопросы по разработке люди
3) известно, что тимлид занимается более важной задачей, чем ваша текущая. Тогда стоит поискать другого специалиста в проблемной области, спросить об этом у коллег.
4) на похожий вопрос уже был получен ответ. Открою небольшой лайфхак на примере код-ревью. Есть простой способ проверить внимательность и понимание кода у автора Pull request: если в коде несколько однотипных багов - указываешь только первый и смотришь, поправил ли он остальные. Тут работает тот же принцип.
Когда можно и нужно спрашивать?
1) вообще не понятна причина проблемы. Опять же по моему опыту на самостоятельное решение такого рода проблем могут уходить дни, и даже недели. Либо делаются правки наугад и проверяются на тестовых стендах. А каждый такой цикл может занимать несколько часов. Или заводятся ошибочные тикеты, их отклоняют или перенаправляют и т.д При этом велика вероятность, что эксперт в данной области решит проблему за пару часов: или подскажет "секретный ингридиент", или скажет, что все надо делать по-другому))), или хотя бы направит к человеку, который сможет проблему решить. Еще важный момент - такое "хождение в потемках" может сильно демотивировать новичка и команду.
2) частный случай предыдущего - куда копать понятно, но есть подозрение, что вам на это потребуется условно день, а тимлид может разъяснить все на пальцах за 10 минут. В этом случае стоит в вопросе упомянуть про свою оценку.
3) тимлид сам предложил подходить к нему по любому вопросу. Не совсем по любому - см. пункты выше про гугление и повторы. Если сомневаетесь в сложности вопроса - об этом тоже можно спросить у лида.
4) сжатые сроки по текущей задаче, задача важная
5) все коллеги указывают на данного человека как на эксперта по вашему вопросу
6) вопрос важный: архитектурный или по структуре БД - и хочется посоветоваться с более опытным коллегой
Вывод: не нужно боятся спрашивать. Но не нужно спрашивать то, что вы как разработчик или должны знать, или можете быстро выяснить сами
#people_interactions
Ситуация: новый разработчик приходит в команды, начинает разбираться с кодом, pipeline, архитектурой, у него возникают вопросы. Когда стоит подойти к более опытному товарищу - тимлиду, техлиду, senior-у - а в каких случаях разобраться самому?
Однозначного ответа нет. Но есть ряд условий.
Когда стоит разобраться самому?
1) проблема не относится к специфике компании. Т.е. это что-то, что скорее всего можно нагуглить на stackoverflow или хабре. По своему опыту скажу, что такие вопросы очень раздражают, уметь гуглить должны все) Далее я рассматриваю случаи, когда вопрос касается специфики компании. Люди, которые спрашивают у коллег: объясни на пальцах как устроены сервлеты - могут стать героями локальных мемов (из собственного опыта)
2) примерно понятно где искать ответ. Если искать лень - для этого существуют чаты разработчиков. Там отвечает либо те, у кого в данный момент есть свободное время, либо те, кто настроен на помощь коллегам. А возможно даже "специально обученные" для ответов на вопросы по разработке люди
3) известно, что тимлид занимается более важной задачей, чем ваша текущая. Тогда стоит поискать другого специалиста в проблемной области, спросить об этом у коллег.
4) на похожий вопрос уже был получен ответ. Открою небольшой лайфхак на примере код-ревью. Есть простой способ проверить внимательность и понимание кода у автора Pull request: если в коде несколько однотипных багов - указываешь только первый и смотришь, поправил ли он остальные. Тут работает тот же принцип.
Когда можно и нужно спрашивать?
1) вообще не понятна причина проблемы. Опять же по моему опыту на самостоятельное решение такого рода проблем могут уходить дни, и даже недели. Либо делаются правки наугад и проверяются на тестовых стендах. А каждый такой цикл может занимать несколько часов. Или заводятся ошибочные тикеты, их отклоняют или перенаправляют и т.д При этом велика вероятность, что эксперт в данной области решит проблему за пару часов: или подскажет "секретный ингридиент", или скажет, что все надо делать по-другому))), или хотя бы направит к человеку, который сможет проблему решить. Еще важный момент - такое "хождение в потемках" может сильно демотивировать новичка и команду.
2) частный случай предыдущего - куда копать понятно, но есть подозрение, что вам на это потребуется условно день, а тимлид может разъяснить все на пальцах за 10 минут. В этом случае стоит в вопросе упомянуть про свою оценку.
3) тимлид сам предложил подходить к нему по любому вопросу. Не совсем по любому - см. пункты выше про гугление и повторы. Если сомневаетесь в сложности вопроса - об этом тоже можно спросить у лида.
4) сжатые сроки по текущей задаче, задача важная
5) все коллеги указывают на данного человека как на эксперта по вашему вопросу
6) вопрос важный: архитектурный или по структуре БД - и хочется посоветоваться с более опытным коллегой
Вывод: не нужно боятся спрашивать. Но не нужно спрашивать то, что вы как разработчик или должны знать, или можете быстро выяснить сами
#people_interactions
Всем привет!
Сегодня хотел бы поднять такую тему - что должно быть в проекте сервиса (в исходниках)?
Кроме собственно кода и файлов с настройками.
1) readme.md
Наличие: обязательно.
Содержимое: описание проекта, его идентификатор (для DevOps pipeline или в вашем реестре сервисов), описание вариантов сборки (отладка, запуск модульных и интеграционных тестов, чистовая сборка), структура каталогов, возможно важные особенности сборки
2) .gitignore
Наличие: обязательно
3) test.http
Наличие: крайне желательно
Содержимое: список URL для отладки на localhost в формате, который понимает IntelliJ IDEA
4) IDEA run configuration
Наличие: крайне желательно
Да, конфигурации тоже можно сохранять. Особенно полезны для больших проектов, допускающих отладку отдельных частей для скорости.
5) скрипты и настройки CI
Наличие: крайне желательно
Часто скрипты меняются вместе с кодом, лучше держать их вместе
6) todo.md
Наличие: желательно
Содержимое: техдолг с точки зрения разработчиков сервиса. Я специально делаю акцент на происхождение, т.к. еще есть внешний техдолг от архитекторов, сопровождения, ИБ, его обычно заводят в JIRA. Почему бы и внутренний техдолг не завести в JIRA? Можно, но есть два риска: если работа идет не по Scrum - задача потеряется в недрах системы, если работа идет по Scrum - слишком рьяный Scrum мастер в рамках гигиены JIRA может настоять на удалении задачи как непонятной\неактуальной. Храня же техдолг вместе с кодом мы можем свободно уточнять формулировки, разбивать на более мелкие задачи для последующего переноса в JIRA с взятием в ближайший спринт.
7) Maven или Gradle wrapper
Наличие: желательно, чтобы не зависеть от наличия утилиты в среде сборки
8) профили checkstyle и ему подобных утилит статического анализа кода
Наличие: возможно, если требуется подстройка для вашего сервиса
9) статическая часть папки .idea
Наличие: возможно по договоренности с командой
10) settings.xml для Maven
Наличие: возможно если они меняются от проекта к проекту
11) документация, естественно через автогенерацию
Наличие: возможно, особенно для библиотек
P.S. Еще может возникнуть вопрос про схемы API - их лучше держать отдельно, чтобы физически разделить интерфейс и реализацию.
#sources
Сегодня хотел бы поднять такую тему - что должно быть в проекте сервиса (в исходниках)?
Кроме собственно кода и файлов с настройками.
1) readme.md
Наличие: обязательно.
Содержимое: описание проекта, его идентификатор (для DevOps pipeline или в вашем реестре сервисов), описание вариантов сборки (отладка, запуск модульных и интеграционных тестов, чистовая сборка), структура каталогов, возможно важные особенности сборки
2) .gitignore
Наличие: обязательно
3) test.http
Наличие: крайне желательно
Содержимое: список URL для отладки на localhost в формате, который понимает IntelliJ IDEA
4) IDEA run configuration
Наличие: крайне желательно
Да, конфигурации тоже можно сохранять. Особенно полезны для больших проектов, допускающих отладку отдельных частей для скорости.
5) скрипты и настройки CI
Наличие: крайне желательно
Часто скрипты меняются вместе с кодом, лучше держать их вместе
6) todo.md
Наличие: желательно
Содержимое: техдолг с точки зрения разработчиков сервиса. Я специально делаю акцент на происхождение, т.к. еще есть внешний техдолг от архитекторов, сопровождения, ИБ, его обычно заводят в JIRA. Почему бы и внутренний техдолг не завести в JIRA? Можно, но есть два риска: если работа идет не по Scrum - задача потеряется в недрах системы, если работа идет по Scrum - слишком рьяный Scrum мастер в рамках гигиены JIRA может настоять на удалении задачи как непонятной\неактуальной. Храня же техдолг вместе с кодом мы можем свободно уточнять формулировки, разбивать на более мелкие задачи для последующего переноса в JIRA с взятием в ближайший спринт.
7) Maven или Gradle wrapper
Наличие: желательно, чтобы не зависеть от наличия утилиты в среде сборки
8) профили checkstyle и ему подобных утилит статического анализа кода
Наличие: возможно, если требуется подстройка для вашего сервиса
9) статическая часть папки .idea
Наличие: возможно по договоренности с командой
10) settings.xml для Maven
Наличие: возможно если они меняются от проекта к проекту
11) документация, естественно через автогенерацию
Наличие: возможно, особенно для библиотек
P.S. Еще может возникнуть вопрос про схемы API - их лучше держать отдельно, чтобы физически разделить интерфейс и реализацию.
#sources
Всем привет!
Хорошая подробная статья про кодировки в Java и что изменилось в Java 18 https://habr.com/ru/companies/jugru/articles/709952/
Еще порадовало, что кто-то заморочился и сделал вот такой навигатор: https://habr.com/ru/articles/147843/ )))
А вывод следующий: в существующих Java сервисах надо явно указывать кодировку, как я и говорил ранее: https://t.me/javaKotlinDevOps/193 В новых написанных на Java 18+ в большинстве случаев можно положится на кодировку по умолчанию - UTF-8. Если конечно вы не храните данные в другой кодировке) Или не работаете с Windows и ее консолью и файловой системой. Или кто-то в вашей команде
не проводит отладку на Windows.
#java #encoding
Хорошая подробная статья про кодировки в Java и что изменилось в Java 18 https://habr.com/ru/companies/jugru/articles/709952/
Еще порадовало, что кто-то заморочился и сделал вот такой навигатор: https://habr.com/ru/articles/147843/ )))
А вывод следующий: в существующих Java сервисах надо явно указывать кодировку, как я и говорил ранее: https://t.me/javaKotlinDevOps/193 В новых написанных на Java 18+ в большинстве случаев можно положится на кодировку по умолчанию - UTF-8. Если конечно вы не храните данные в другой кодировке) Или не работаете с Windows и ее консолью и файловой системой. Или кто-то в вашей команде
не проводит отладку на Windows.
#java #encoding
Хабр
JEP-400 или UTF-8 РєРѕРґРёСЂРѕРІРєР° РїРѕ умолчанию
Не прошло и пяти лет, как в Java 18 докатилось небольшое, но очень ожидаемое и обсуждаемое изменение: теперь во всех стандартных API используется UTF-8 кодировка по умолчанию. Это изменение, которое...
Всем привет!
Я уже писал о проблеме интероперабельности Kotlin-Java касающейся null safety - https://t.me/javaKotlinDevOps/190
Есть и еще одна, коллега столкнулся с ней недавно.
В Kotlin четко разделяет изменяемые и неизменяемые коллекции на уровне типов, по умолчанию предлагая делать их неизменяемыми.
В Java наоборот - по умолчанию все коллекции изменяемые, хотя неизменяемые создать тоже можно, например, Collections.unmodifiableXXX, но это не единственный способ.
Итого: предположу, что в Kotlin условно 90% коллекций немодифицируемые, в Java - наоборот 90% модифицируемые.
Что же будет при передаче коллекции из Kotlin в Java?
Да, хорошая практика независимо от языка - не менять передаваемые в метод параметры. Но эта практика не стимулируется к использованию Java.
Да, иммутабельность можно проверить, но делается это довольно криво и я подозреваю делается редко: https://ru.stackoverflow.com/questions/608545/collections-unmodifiable-как-определить
Отсюда получаем, что с большой вероятностью возможна ошибка UnsupportedOperationException на вставке в эти коллекции в Java.
#java #kotlin
Я уже писал о проблеме интероперабельности Kotlin-Java касающейся null safety - https://t.me/javaKotlinDevOps/190
Есть и еще одна, коллега столкнулся с ней недавно.
В Kotlin четко разделяет изменяемые и неизменяемые коллекции на уровне типов, по умолчанию предлагая делать их неизменяемыми.
В Java наоборот - по умолчанию все коллекции изменяемые, хотя неизменяемые создать тоже можно, например, Collections.unmodifiableXXX, но это не единственный способ.
Итого: предположу, что в Kotlin условно 90% коллекций немодифицируемые, в Java - наоборот 90% модифицируемые.
Что же будет при передаче коллекции из Kotlin в Java?
Да, хорошая практика независимо от языка - не менять передаваемые в метод параметры. Но эта практика не стимулируется к использованию Java.
Да, иммутабельность можно проверить, но делается это довольно криво и я подозреваю делается редко: https://ru.stackoverflow.com/questions/608545/collections-unmodifiable-как-определить
Отсюда получаем, что с большой вероятностью возможна ошибка UnsupportedOperationException на вставке в эти коллекции в Java.
#java #kotlin
Telegram
(java || kotlin) && devOps
Всем привет!
Сегодня пост о крутой фиче Kotlin, которая решила одну важную проблему. И добавила другую)
Я о Null safety.
Суть ее в том, что в Kotlin любой тип представлен в двух ипостасях - одна может содержать null значения, другая - нет.
String - не может…
Сегодня пост о крутой фиче Kotlin, которая решила одну важную проблему. И добавила другую)
Я о Null safety.
Суть ее в том, что в Kotlin любой тип представлен в двух ипостасях - одна может содержать null значения, другая - нет.
String - не может…