Boilerplate — to kod szablonowy, który programiści piszą z minimalnymi zmianami w każdym nowym module lub projekcie. Nie zawiera unikalnej logiki biznesowej, a jedynie przygotowuje infrastrukturę: konfigurację, podłączenie bibliotek, standardowe procedury obsługi i klasy DTO. Według raportu CodeScene Engineering Productivity Report (2025), boilerplate stanowi od 20 do 40 procent całego kodu w typowej aplikacji komercyjnej. Główny problem takiego kodu nie polega na tym, że się powtarza, ale na tym, że każde powtórzenie to punkt awarii: błąd w jednej kopii nie jest synchronizowany z innymi, a będy rozprzestrzeniają się po projekcie. Automatyzacja generowania boilerplate’u poprzez generowanie kodu, adnotacje i makra to jeden z najskuteczniejszych sposobów na przyspieszenie rozwoju bez utraty jakości.
Najważniejsze
Boilerplate (kod szablonowy) to fragmenty kodu źródłowego, które powtarzają się w różnych częściach projektu z minimalnymi zmianami. Termin pochodzi z drukarstwa, gdzie boilerplate nazywano szablonowe teksty dla gazet, których nie trzeba było przepisywać. W programowaniu jest to każdy kod, który trzeba pisać w kółko, aby spełnić wymagania frameworka, języka lub architektury.
Boilerplate nie jest długiem technicznym w klasycznym rozumieniu — nie zawiera błędów ani nie narusza zasad SOLID. Zwiększa jednak objętość kodu, który trzeba utrzymywać, testować i czytać. Każda linia boilerplate’u to potencjalne miejsce na literówkę, której kompilator nie zawsze może wychwycić.
Według raportu JetBrains Developer Ecosystem (2025), 67 procent programistów uważa boilerplate za główną przyczynę spadku produktywności. W rozwoju mobilnym ta liczba jest wyższa: projekty Android w Javie zawierają znaczną ilość kodu szablonowego dla findViewById, Intentów, adapterów RecyclerView i ContentProviderów. Kotlin i Swift rozwiązały część tych problemów za pomocą środków składniowych, ale boilerplate nie zniknął całkowicie.
Projektując architekturę, staraj się wybierać rozwiązania, które minimalizują kod szablonowy. Na przykład zamiast ręcznego pisania implementacji Parcelable użyj @Parcelize w Kotlin. Zamiast fabryk dla ViewModel — Hilt z @HiltViewModel. Każda taka optymalizacja oszczędza godziny pracy na skalę projektu.
Najbardziej rozpoznawalnym przykładem boilerplate’u w rozwoju Androida jest RecyclerView.Adapter. Przed pojawieniem się Kotlin i ViewBinding każdy adapter wymagał około 80–100 linii kodu szablonowego: onCreateViewHolder, onBindViewHolder, getItemCount, wewnętrzna klasa ViewHolder, konstruktor, wiązanie pól. Dzięki ViewBinding kod został skrócony, ale nie zniknął całkowicie.
class UserAdapter(
private val users: List<User>
) : RecyclerView.Adapter<UserAdapter.ViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(R.layout.item_user, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
holder.bind(users[position])
}
override fun getItemCount(): Int = users.size
class ViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
fun bind(user: User) {
Glide.with(itemView)
.load(user.avatarUrl)
.into(itemView.avatar)
}
}
}
Innym przykładem jest mapowanie JSON w Javie bez bibliotek. Ręczne parsowanie odpowiedzi API wymaga napisania dziesiątków metod, z których każda sprawdza obecność klucza, pobiera wartość i przypisuje ją do pola. Dzięki bibliotekom takim jak Gson, Moshi czy Kotlin Serialization to jedna adnotacja @Serializable.
W rozwoju iOS klasycznym boilerplate’em jest implementacja CodingKey i Decodable dla każdej odpowiedzi API, szczególnie gdy klucze JSON różnią się od camelCase’owych nazw właściwości. Mimo automatycznej generacji Codable, ręczne wymienianie CodingKeys pozostaje źródłem kodu szablonowego.
Używaj generowania kodu do tworzenia boilerplate’u na etapie kompilacji. W Androidzie — Annotation Processing (KSP) dla Room, Dagger, Moshi. W iOS — Sourcery dla Codable i AutoMockable. Za każdą godzinę poświęconą na konfigurację generatora oszczędzasz dni ręcznego kopiowania.
Boilerplate szkodzi projektowi na trzy sposoby: spowalnia pisanie nowej funkcjonalności, utrudnia czytanie istniejącego kodu i tworzy punkty desynchronizacji przy zmianach.
Spowolnienie rozwoju jest oczywiste: programista traci czas na pisanie kodu, który nie zawiera logiki biznesowej. Zamiast realizować nową funkcję (na przykład dodanie pola w profilu użytkownika), pisze migrację bazy danych, klasę DTO, mapper do encji domenowej, ekran z polem wprowadzania, walidację i testy dla każdej warstwy. Większość tej pracy jest mechaniczna.
Desynchronizacja to bardziej podstępny problem. Gdy w jednym miejscu zmienia się struktura danych (na przykład dodawane jest pole w odpowiedzi API), programista musi zaktualizować DTO, mapper, model, ekran i testy. Jeśli któreś miejsce zostanie pominięte, aplikacja się kompiluje, ale pada w czasie wykonania lub — co gorsza — wyświetla nieprawidłowe dane bez błędu. Im więcej warstw boilerplate’u, tym większe prawdopodobieństwo takiej desynchronizacji.
Analizuj projekt pod kątem powtarzających się wzorców. Jeśli widzisz trzy identyczne klasy z różnymi nazwami — to kandydat do generowania. Wdrażaj generowanie kodu jako część rozwiązania architektonicznego, a nie jako jednorazową optymalizację. To się opłaca przy każdym nowym module.
Generowanie kodu to najbardziej niezawodny sposób walki z boilerplate’em. Zamiast ręcznego pisania kodu szablonowego programista opisuje metadane (adnotacje, schematy, konfiguracje), a generator tworzy gotowy kod na etapie kompilacji.
W ekosystemie Android standardowym narzędziem do generowania kodu jest KSP (Kotlin Symbol Processing). Zastępuje on przestarzałe KAPT i działa szybciej dzięki bezpośredniemu dostępowi do AST Kotlina bez generowania Java stubs. KSP jest używany przez Room (generowanie implementacji DAO), Moshi (generowanie JsonAdapter), Glide (generowanie klas docelowych ładowania) i Dagger (generowanie grafu DI).
@Entity(tableName = "users")
data class UserEntity(
@PrimaryKey val id: Long,
@ColumnInfo(name = "full_name") val name: String,
@ColumnInfo(name = "avatar_url") val avatarUrl: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getById(@Param("id") id: Long): UserEntity?
}
W rozwoju iOS rolę generowania kodu pełni Sourcery — narzędzie, które przetwarza szablony Stencil i generuje kod Swift na podstawie adnotacji w komentarzach. Typowe scenariusze: AutoMockable (generowanie mocków do testów), AutoCodable (generowanie implementacji Decodable bez CodingKeys), AutoEquatable i AutoLenses.
Dla projektów Flutter boilerplate jest redukowany przez generatory poprzez build_runner: json_serializable do mapowania JSON, freezed do niezmiennych modeli z copyWith, retrofit_generator dla klientów API i injectable_generator dla DI. Każdy z tych generatorów zamienia 10–20 linii adnotacji w setki linii gotowego kodu.
Adnotacje i makra to deklaratywny sposób wskazania kompilatorowi lub preprocesorowi, jaki kod ma zostać wygenerowany. Programista nie pisze implementacji, a jedynie zaznacza intencję, a generator zamienia znaczniki w gotowy kod.
Najlepszym przykładem jest Lombok w Javie (historycznie) i Kotlin data class. Data class w Kotlin automatycznie generuje equals, hashCode, toString, componentN i copy — w Javie wymagałoby to około 80 linii kodu ręcznie lub użycia Lombok z @Data. Kotlin rozwiązał problem na poziomie języka, czyniąc boilerplate niejawnym.
W Swift podobną rolę pełnią makra (Swift Macros, wprowadzone w Swift 5.9). Zamiast ręcznego pisania implementacji Codable, programista oznacza strukturę @Codable — a kompilator sam generuje niezbędny kod. Inne wbudowane makra: @Observable (stan obserwowalny), @ResultBuilder (konstruktory wyników) i @MainActor (dyspozycja na główny wątek).
@Codable
struct UserProfile {
let id: Int
let displayName: String
let avatarURL: URL
let bio: String?
}
// Makro @Codable generuje:
// extension UserProfile: Codable { }
// private enum CodingKeys: String, CodingKey {
// case id, displayName, avatarURL, bio
// }
Wybierając między generowaniem kodu a makrami, preferuj makra, jeśli język je obsługuje. Makra działają na poziomie kompilatora, nie wymagają konfiguracji skryptów budowania, nie spowalniają kompilacji (w przeciwieństwie do Annotation Processing) i są zawsze zsynchronizowane z kodem źródłowym. Jeśli makra są niedostępne — używaj zewnętrznych generatorów przez KSP, Sourcery lub build_runner.
Każdy język i platforma oferują własne narzędzia do minimalizacji boilerplate’u. Poniżej znajdują się konkretne praktyki dla głównych stosów rozwoju mobilnego.
| Platforma | Narzędzie / Technika | Co zastępuje |
|---|---|---|
| Android / Kotlin | data class | equals, hashCode, toString, copy, componentN |
| Android / Kotlin | @Parcelize | Implementację Parcelable |
| Android / Kotlin | ViewBinding / DataBinding | findViewById, ButterKnife |
| iOS / Swift | Codable + makra | Ręczne parsowanie JSON, CodingKeys |
| iOS / Swift | Sourcery | AutoMockable, AutoEquatable, AutoLenses |
| Flutter / Dart | freezed + json_serializable | copyWith, klasy sealed, equals/hashCode, JSON |
| Flutter / Dart | retrofit_generator | Klient API z typami zapytań i odpowiedzi |
Dla frontendu webowego (React Native / TypeScript) głównym narzędziem jest generowanie typów ze specyfikacji OpenAPI (openapi-typescript, swagger-codegen). Każdy endpoint automatycznie otrzymuje typowane zapytanie i odpowiedź — programista nie musi ręcznie opisywać interfejsów dla setek wywołań API.
Wdrażaj generowanie kodu na wczesnych etapach projektu. Migracja istniejącego projektu na generatory jest trudniejsza niż projektowanie z ich użyciem od podstaw. Jeśli projekt jest już napisany — zacznij od najbardziej bolesnego punktu: Java → Kotlin (data class), ręczne adaptery → ListAdapter z DiffUtil, ręczne mapowanie JSON → Moshi / Kotlin Serialization.
Często zadawane pytania
Boilerplate to nie dług, a nadmiarowość: kod jest poprawny, ale jest go zbyt dużo. Dług techniczny to świadome kompromisowe rozwiązanie, które będzie wymagało naprawy. Boilerplate nie wymaga naprawy — wymaga automatyzacji.
Nie, w małych projektach boilerplate może być uzasadniony prostotą: jest od razu widoczny i łatwy do zmiany. Problem pojawia się na skalę — gdy podobnych modułów jest więcej niż dziesięć, ręczne kopiowanie przestaje być efektywne i czas wdrożyć generowanie.
Kod zależny od zewnętrznych usług o niestandardowej logice (niestandardowe SDK, zastrzeżone protokoły) jest trudny do wygenerowania. W takich przypadkach boilerplate pisze się ręcznie, ale wydziela do osobnych modułów, aby zminimalizować rozrzut po projekcie.
W nowych projektach lepiej od razu przejść na Kotlin, gdzie data class rozwiązuje te same zadania na poziomie języka. Jeśli projekt pozostaje w Javie — Lombok pozostaje standardem de facto, ale pamiętaj, że wymaga wtyczki do IDE i może powodować konflikty z nowymi wersjami Java.
Tak, generowanie kodu dodaje czas do kompilacji. KSP działa szybciej niż KAPT, ale wciąż dodaje sekundy lub minuty do pełnej kompilacji. Optymalizacja: używaj kompilacji inkrementalnej i buforowania wyników generowania między kompilacjami.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również