Coraz więcej osób zgłasza się do mnie poszukując pomocy w przejściu ze stanowiska Junior Java Developer na Mid-Level Java Developer. Chcąc pomóc jak największej liczbie osób w tym artykule przedstawię najczęściej zadawane mi pytania oraz moje podejście do tematu przejścia z Junior na Mid-Level Java Developer.

Część osób, które zgłaszają się do mnie czują, że stoją w miejscu, nie rozwijają się, to ich motywuje do zmiany. Spora część osób zgłasza się do mnie, co jest smutne, ponieważ nie otrzymuje właściwego wsparcia w projekcie od doświadczonych programistów. Dobra informacja jest taka, że każda z tych osób może przejść od junior do mid-level.

Pomijając kwestie nazewnictwa Junior vs. Mid-Level, to należy pamiętać, że w tym wszystkim chodzi o doskonalenie warsztatu programistycznego. Dodatkowo każda firma inaczej nazywa stanowiska, np.: Młodszy Specjalista ds. Rozwoju Oprogramowania, Junior Java Developer, Software Engineer. Dlatego w tym artykule duży nacisk położę na kwestię doskonalenia.

Najczęściej zadawane pytania:

  1. Po ilu latach mogę zostać Mid-Level Java Developerem, Developerką?
  2. Jaki zakres wiedzy muszę posiadać?
  3. Ile projektów muszę zrealizować?
  4. Jak uzyskać wsparcie w projekcie od doświadczonych programistów?

Po ilu latach mogę zostać Mid-Level Java Developerem, Developerką? Granica przejścia z Junior na Mid-Level Java Developer jest bardzo umowna. Moim zdaniem nie zależy ona od stażu pracy w latach, najważniejsza jest „ilość” oraz różnorodność projektów, w których Junior Java Developer, Developerka brali udział.

Pytania „Jaki zakres wiedzy muszę posiadać?” oraz „Ile projektów muszę zrealizować?” zamieniłbym na jedno zasadnicze pytanie: Jak zapewnić sobie różnorodność projektów w firmie? Zacznijmy od tego, że różne projekty pozwalają na pracę z wieloma technologiami i narzędziami. Najważniejsze, to nie zamykać się we własnej strefie komfortu, działając w dobrze nam znanym i bezpiecznym projekcie.

Różnorodność można znaleźć nawet w obrębie jednego projektu, implementując jego różne moduły/funkcjonalności. Pracując przy np.: module przelewów bankowych można „przejść” do modułu odpowiedzialnego za generowanie i/lub archiwizację dokumentów bankowych – potwierdzenie przelewu, wyciąg z karty kredytowej.

Zazwyczaj projekty wchodzą w skład większego systemu informatycznego, każdy projekt jest realizowany przez inny dział w firmie, to stwarza możliwość zmiany projektu w ramach różnych działów. Pracując przy np. projekcie billingowym dla operatora telekomunikacyjnego można przejść do działu wsparcia sprzedaży urządzeń.

Jak uzyskać wsparcie w projekcie od doświadczonych programistów? Zakładając, że wychodzimy z pozycji Junior Java Developera, Developerki z kilkunastoma miesiącami doświadczenia oraz ze znajomością elementów opisanych w Jak powinny wyglądać realne wymagania dla Junior Java Developer’a? – Just Join IT – Java, git, Maven/Gradle, JUnit5, HTTP, REST, Spring Framework, Hibernate ORM.

Temat uzyskania wsparcia od doświadczonych programistów jest dość złożony, bo chodzi tu o interakcję z drugim człowiekiem. Mogę podać jeden sprawdzony sposób. Dla tworzonego kodu – umieszczanego na nowym branchu w git – tworzymy Pull Request/Megre Request, przypisując doświadczonych kolegów jako osoby do review – przegląd, weryfikacja naszego kodu. Można również wykorzystać (w granicach rozsądku) wszystkie dostępne środki komunikacji, np.: chat, video, telefon oraz rozmowa w cztery oczy.


Moje podejście do tematu wsparcia przejścia z Junior do Mid-Level Java Developer.

  1. Dogłębne poznanie wykorzystywanych frameworków oraz narzędzi:
  2. Testy jednostkowe, mockowanie, idealnie TDD.
  3. Rozbicie, wyizolowanie istoty problemu do postaci nowego oddzielnego projektu.
  4. Zadania – https://www.codewars.com/
  5. Wyzwania – https://challengerocket.com/
  6. „Get Trained For The Future, Today” – https://careerkarma.com/

Całość kodu źródłowego można znaleźć na moim koncie GitHub pod adresem https://github.com/juniorjavadeveloper-pl/java-junior-transition-mid-level

Część prezentowanego kodu źródłowego w samym artykule może być w wersji „okrojonej”, tak, aby przedstawić istotę zagadnienia bez zaciemniania obrazu szczegółami. Cały kod źródłowy jest na GitHub.

Dogłębne poznanie wykorzystywanych frameworków oraz narzędzi pozwala pokazać jak kiedyś wyglądało „programowanie”. To pozwala zobaczyć jak wiele ułatwień dają współczesne rozwiązania. Pozwala również lepiej zrozumieć dlaczego coś działa tak, a nie inaczej oraz jak lepiej wykorzystać technologię i rozwiązać pojawiające się błędy.

Spring MVC wykorzystuje Java Servlet, technologia Servletów ma 26 lat – Jakarta Servlet – wchodzi w skład Java EE – Java EE, to obecnie Jakarta EE po “oddelegowaniu projektu” przez firmę Oracle do Eclipse Foundation. Używając Java/Jakarta EE trzeba „zainstalować” serwer webowy obsługujący protokuł HTTP – Apache Tomcat – skonfigurować go, dodać plugin do Maven i już można uruchamiać aplikacje web. Korzystając ze Spring MVC + Spring Boot wystarczy wygenerować aplikację web – Spring Initializr – następnie po prostu uruchomić aplikację.

Web on Servlet Stack – 1. Spring Web MVC

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, „Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more commonly known as „Spring MVC”. – https://docs.spring.io/spring-framework/docs/6.0.7/reference/html/web.html#mvc

Poniższy kod pokazuje jak obsłużyć żądanie protokołu HTTP dla metody GET wraz z przesłaniem parametru żądania. Dla technologi Java/Jakarta EE potrzebujemy klasy Java NotThatSimpleJavaServlet oraz pliku web.xml z konfiguracją. Dla Spring MVC wystarczy jedna klasa Java SimpleSpringController. W przypadku Java/Jakarta Servlet niepotrzebnie wiążemy się z klasą HttpServlet – poprzez dziedziczenie, extends – jak wiemy w języku Java nie ma wielodziedziczenia, obsługa wyjątków jest utrudniona.


import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class NotThatSimpleJavaServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        String dashboardMode = request.getParameter("dashboardMode");
        if (dashboardMode != null) {
            request.setAttribute("dashboardMode", dashboardMode);
        }

        RequestDispatcher requestDispatcher = 
                request.getRequestDispatcher("/WEB-INF/views/dashboard.jsp");
        requestDispatcher.forward(request, response);
    }
}

Obsługa end-point, np.: http://localhost:8080/dashboard/java/servlet/ – połączenie klasy Java NotThatSimpleJavaServlet oraz pliku konfiguracyjnego web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee 
            https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0"
         metadata-complete="false">

    <servlet>
        <servlet-name>NotThatSimpleJavaServlet</servlet-name>
        <!-- NOTE: package name shortened for readability -->
        <servlet-class>
            pl.juniorjavadeveloper.java.mvc_java_servlet.NotThatSimpleJavaServlet
        </servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>NotThatSimpleJavaServlet</servlet-name>
        <url-pattern>/dashboard/java/servlet/*</url-pattern>
    </servlet-mapping>
</web-app>

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(value = "/dashboard/simple/spring")
public class SimpleSpringController {
    @GetMapping
    public String dashboardView(String dashboardMode, Model model) {
        if (dashboardMode != null) {
            model.addAttribute("mode", dashboardMode);
        }
        return "dashboard.html";
    }
}

Obsługa end-point, np.: http://localhost:8080/dashboard/simple/spring/ – wymaga tylko jednej klasy Java SimpleSpringController – konfiguracja dostarczona przez Spring Boot.


Hibernate wykorzystuje JDBC (Java Database Connectivity), co oznacza, że warto wiedzieć, co dzieje się „pod spodem”. Poznanie czystego JDBC wymusza również znajomość SQL, co jest bardzo ważnym elementem w warsztacie Mid-Level Java Developera, Developerki. Dodatkowo, jeżeli nam na wydajności operacji bazodanowych, to JDBC jest bardzo dobrym rozwiązaniem. Należy pamiętać, że mamy więcej możliwości utrwalania danych w Java – więcej szczegółów w artykule https://dev.to/yigi/hibernate-vs-jdbc-vs-jpa-vs-spring-data-jpa-1421. Kod źródłowy z przykładami wykorzystania Hibernate można znaleźć na moim GitHub – https://github.com/juniorjavadeveloper-pl/hibernate-examples/.

1. Architecture – 1.1. Overview

Hibernate, as an ORM solution, effectively „sits between” the Java application data access layer and the Relational Database, as can be seen in the diagram above. – https://docs.jboss.org/hibernate/orm/6.2/userguide/html_single/Hibernate_User_Guide.html#architecture-overview

CREATE TABLE EMPLOYEES(
    ID INT PRIMARY KEY,
    FIRST_NAME VARCHAR(255),
    LAST_NAME VARCHAR(255),
    DEPARTMENT VARCHAR(255)
);

Korzystając z JDBC należy samemu utworzyć elementy w bazie danych, np.: tabele. Powyższy skrypt SQL tworzy tabelę o nazwie EMPLOYEES.

public class EmployeeModel {
    private Long id;

    private String firstName;
    private String lastName;

    private String department;

    // getters/setters
}

Potrzebujemy również klasę POJO, która będzie odpowiadać strukturze tabeli w bazie danych, ale klasę POJO musimy samodzielnie wypełnić za pomocą klasy DAO – w odróżnieniu od Hibernate, gdzie dzieje się, to automatycznie.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class EmployeeModelDao {

    private static final String DB_URL =
            "jdbc:h2:~/h2database/hibernate-orm-jdbc-database";
    private static final String INSERT_EMPLOYEE_SQL =
            "INSERT INTO EMPLOYEES(ID, FIRST_NAME, LAST_NAME, DEPARTMENT)" +
                    " VALUES(?, ?, ?, ?);";

    public void create(EmployeeModelJDBC employeeModel) {
        // NOTE: Connection code must be moved to a separate class and reused!
        try (Connection connection = DriverManager.getConnection(DB_URL, "sa", "");
             PreparedStatement preparedStatement =
                     connection.prepareStatement(INSERT_EMPLOYEE_SQL)) {

            preparedStatement.setLong(1, employeeModel.getId());
            preparedStatement.setString(2, employeeModel.getFirstName());
            preparedStatement.setString(3, employeeModel.getLastName());
            preparedStatement.setString(4, employeeModel.getDepartment());

            preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Dla JDBC trzeba stworzyć klasę DAO (Data Access Object) – oddzielną dla każdej tabeli w bazie danych. Klasa DAO będzie zawierała operacje CRUD na tabeli, dla SQL będą, to INSERT, SELECT, UPDATE oraz DELETE.


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">org.h2.Driver</property>
        <property name="connection.url">jdbc:h2:~/hibernate-database</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"></property>
        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>
        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.H2Dialect</property>
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>

        <!-- Names the annotated entity class -->
        <!-- NOTE: package name shortened for readability -->
        <mapping class="pl.juniorjavadeveloper.java.orm_jdbc.UserEntity"/>
    </session-factory>
</hibernate-configuration>

Dla Hibernate ORM trzeba jednorazowo stworzyć plik konfiguracyjny dla połączenia z bazą danych oraz utrwalanych encji.

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
@Table(name = "USERS")
public class UserEntity {
    @Id
    @GeneratedValue
    private Long id;

    @Column(unique = true)
    private String login;
    private String password;

    @Column(name = "FIRST_NAME")
    private String firstName;
    @Column(name = "LAST_NAME")
    private String lastName;

    public UserEntity() {
    }

    public UserEntity(String login, String password) {
        this.login = login;
        this.password = password;
    }
}

W Hibernate operujemy pojęciem @Entity, które pozwalają za pomocą adnotacji odwzorować elementy klasy Java (również POJO) na strukturę tabeli w bazie danych. Dla powyższej encji Hibernate automatycznie stworzy tabelę o nazwie USERS.

public class UserEntityDao {
    private static SessionFactory sessionFactory;

    // NOTE: this code must be moved to a separate class and then should be reused!
    static {
        StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
                .configure("hibernate.cfg.xml")
                .build();
        try {
            sessionFactory = new MetadataSources(serviceRegistry)
                    .buildMetadata()
                    .buildSessionFactory();
        } catch (Exception e) {
            e.printStackTrace();
            StandardServiceRegistryBuilder.destroy(serviceRegistry);
        }
    }

    public void create(UserEntity userEntity) {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.save(userEntity);
        session.getTransaction().commit();
        session.close();
    }
}

DAO dla Hibernate będzie używać pojęcia sesji oraz transakcji, które będą „oplatały” operacje CRUD na obiektach encji. Dla różnych encji, np.: Student, Notebook, Employee będą używane te same metody do operacji na encjach, np.: SQL INSERT, to metoda save().


Java Properties zamiast Spring @Value. Korzystając z dobrodziejstwa Spring Framework można łatwo pominąć fakt, że pewne elementy konfiguracyjne w pliku application.properties muszą być w jakiś sposób załadowane i odczytane. Do tego dochodzi fakt, że takie elementy konfiguracyjne muszą być różne dla różnych środowisk, np. dev, test, prod. Poniżej prezentuję czysto Javowe rozwiązanie w kwestii odczytu klucz/wartość z plików properties oraz jego wykorzystanie w DAO z użyciem JDBC.

import java.io.IOException;
import java.util.Properties;

public class JdbcPropertiesHelper {
    private static final Properties properties = new Properties();

    static {
        try {
            properties.load(ClassLoader.getSystemClassLoader()
                    .getResourceAsStream("jdbc.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String getProperty(String key) {
        return properties.getProperty(key);
    }

    public static String getProperty(String key, String defaultValue) {
        return properties.getProperty(key, defaultValue);
    }
}

Powyżej przykładowa implementacja własnego mechanizmu odczytu plików properties.

// JdbcPropertiesHelper example usage in EmployeeModelDao.
try (Connection connection = DriverManager.getConnection(
        JdbcPropertiesHelper.getProperty("jdbc.url"),
        JdbcPropertiesHelper.getProperty("jdbc.username"),
        JdbcPropertiesHelper.getProperty("jdbc.password"));
     PreparedStatement preparedStatement =
             connection.prepareStatement(INSERT_EMPLOYEE_SQL,
                     Statement.RETURN_GENERATED_KEYS)) {
}

Odczyt parametrów konfiguracji połączenia z bazą danych za pomocą plików properties pozwala nam odseparować się od konfiguracji oraz dostarczyć klucz/wartość dla różnych środowisk, np.: dev, test.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource("classpath:jdbc.properties")
public class SimpleSpringValueProperties {
    @Value("${jdbc.url}")
    private String jdbcUrl;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    // getters/setters
}

Powyżej kod pokazujący jak łatwo i auto-magicznie odczytywanie klucz/wartość można zaimplementować w Spring Framework.


„Nakładanie” Spring DI w czystym OOP. Na temat „DI w czystym OOP” pisałem już w artykule Trzy trójce programistyczne – jak ułatwić początki programowania, wykorzystam przedstawione tam klasy Java, aby dodać elementy Springa. Dlaczego użyłem @Scope(value = „prototype”) ? – https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes.

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

@Component
@Scope(value = "prototype")
public class Notebook {

    protected int pages;
    protected int currentPage;
    protected Map<Integer, String> contents = new HashMap<>();

    // NOTE: skipped methods for code clarity.
}

Powyżej zwykła klasa Java „zamieniona” na @Component, Bean Springowy. Kod w Spring Framework, który nałożyłem na przykład z kodem w czystej Javie.

@Component
@Scope(value = "prototype")
public class Student {

    private final Notebook notebook;

    public Student(Notebook notebook) {
        this.notebook = notebook;
    }

    // NOTE: skipped methods for code clarity.
}

Powyżej zwykła klasa Java „zamieniona” na @Component, Bean Springowy.


Spring Framework bez Spring Boot. Dla powyższego kodu idealnie można przedstawić aplikację napisaną z użyciem Spring Framework, ale bez wykorzystania Spring Boot. Tak, można tworzyć aplikacje w „czystym” Spring Framework bez użycia Spring Boot, kiedyś była, to jedyna dostępna opcja. Obecnie, to podejście można wykorzystać do nieszablonowego połączenia aplikacji napisanych w języku Java, np.: Spring Framework + JavaFX – funkcjonalność DI i IoC w połączeniu z aplikacją typu desktop.

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringApplicationWithoutSpringBoot {
    public static void main(String[] args) {
        ApplicationContext applicationContext =
                new AnnotationConfigApplicationContext(
                        "pl.juniorjavadeveloper.java.juniortransitionmid" +
                                ".deepunderstanding.spring");

        Student firstStudentBean = applicationContext.getBean(Student.class);
        firstStudentBean.notebookPages(100);

        Student secondStudentBean = applicationContext.getBean(Student.class);
        secondStudentBean.notebookPages(60);
        secondStudentBean.startNote("Hello World!", 11);
    }
}

Powyżej kod Java tworzący kontekst aplikacji Spring ApplicationContext dla opcji z adnotacjami AnnotationConfigApplicationContext. Po stworzeniu kontekstu aplikacji, dostęp do Beanów Springowych uzyskujemy za pomocą metody getBean().

kwi 10, 2023 9:14:36 PM pl.jjd.j.j.d.spring.di_oop.Student notebookPages
INFO: notebookPages(100)
kwi 10, 2023 9:14:36 PM pl.jjd.java.j.d.spring.di_oop.Notebook setPages
INFO: setPages(100)
kwi 10, 2023 9:14:36 PM pl.jjd.java.j.d.spring.di_oop.Student notebookPages
INFO: notebookPages(60)
kwi 10, 2023 9:14:36 PM pl.jjd.java.j.d.spring.di_oop.Notebook setPages
INFO: setPages(60)
kwi 10, 2023 9:14:36 PM pl.jjd.java.j.d.spring.di_oop.Student startNote
INFO: startNote(Hello World!, 11)
kwi 10, 2023 9:14:36 PM pl.jjd.java.j.d.spring.di_oop.Notebook goToPage
INFO: startNote(11)
kwi 10, 2023 9:14:36 PM pl.jjd.java.j.d.spring.di_oop.Notebook writeNoteContent
INFO: writeNoteContent(Hello World!)
kwi 10, 2023 9:14:36 PM pl.jjd.java.j.d.spring.di_oop.Notebook writeNoteContent
INFO: Notebook - append note content to the current page: Hello World!

Process finished with exit code 0

Testy jednostkowe, mockowanie, idealnie TDD. Zapomnij o „testowaniu kodu” za pomocą metody main(). Pisanie testów jednostkowych, to podstawa, jeżeli „chcemy spać spokojnie”. Jeżeli ktoś pisze testy jednostkowe, to znaczy, że rozumie tworzony system informatyczny. Koleżanka, kolega z zespołu, dwa razy się zastanowi zanim zmodyfikuje kod źródłowy, a modyfikacja spowoduje błędy w testach – chyba, że znają i stosują „model Duński” w testach 😉

Jeżeli chcemy przetestować czy działa nowo dodana encja Hibernate, to szaleństwem jest budowanie, uruchamianie aplikacji, następnie logowanie się do systemu, aby wpisać dane w formularzu na stronie web, a koniec śledzenie błędów w logach. W tej sytuacji potrzebne są testy jednostkowe bądź integracyjne. Więcej o testach jednostkowych i nie tylko można poczytać na https://martinfowler.com/testing/.

W sytuacji, kiedy nie mamy dostępu do usług zewnętrznych, np.: api firmy trzeciej, a nasz kod źródłowy, tego wymaga, to piszemy testy z wykorzystaniem mocków. Nie jest, to łatwe, ale jeżeli raz poznamy ideę mockowania, to będzie nam dużo łatwiej. Najczęściej po prostu nie chemy modyfikować pewnych zasobów, a przetestować nasz nowy kod. Przykładem jest mock dla repository w service.

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class EmployeeServiceMockTest {

    private static final long EMPLOYEE_MODEL_ID_1 = 1L;

    @Test
    void register() {
        // given
        EmployeeModelDao employeeModelDaoMock = Mockito.mock(EmployeeModelDao.class);
        EmployeeService employeeService = new EmployeeService(employeeModelDaoMock);

        EmployeeModel employeeModel = new EmployeeModel(null, "Tim", "Cook", "Sales");
        EmployeeModel registeredEmployeeModelMock = 
                new EmployeeModel(EMPLOYEE_MODEL_ID_1, "Tim", "Cook", "Sales");

        // when
        Mockito.when(employeeService.register(employeeModel))
                .thenReturn(registeredEmployeeModelMock);
        EmployeeModel registeredEmployeeModel = 
                employeeService.register(employeeModel);

        // then
        Assertions.assertAll(
                () -> Assertions.assertNotNull(registeredEmployeeModel,
                        "registeredEmployeeModel is NULL"),
                () -> Assertions.assertNotNull(registeredEmployeeModel.getId(),
                        "registeredEmployeeModel ID is NULL"),
                () -> Assertions.assertEquals(EMPLOYEE_MODEL_ID_1, 
                        registeredEmployeeModel.getId(),
                        "registeredEmployeeModel ID is not equals")
        );
    }
}

Powyższy kod źródłowy prezentuje test jednostkowy z użyciem mocków


Rozbicie, wyizolowanie istoty problemu do postaci nowego oddzielnego projektu. Temat bardzo obszerny, a sposobów na jego rozwiązanie wiele. Dlatego nie będę przedstawiał kodu źródłowego, ale chcę umieścić ten punkt w moim artykule z prostego powodu. Na co dzień w pracy programiści, programistki zajmują się dużymi i złożonymi systemami, które mają wiele zależności. Skupiając się na jednym konkretnym wycinku tracimy z oczy szerszy kontekst.

Dla przykładu, w projekcie do istniejących @Entity mamy dodać nową encję, która będzie miała zależności do już istniejących encji. Dodajemy jedną tylko klasę encji i relację, a projekt przestaje nam działać. Kontekst Springa nie inicjalizuje się, Hibernate mówi, że nie „widzi” encji, którą dodaliśmy.

Co ja bym zrobił w takim przypadku? Wygenerował nowy projekt na start.spring.io, użył IntelliJ w wersji Community, dodał minimalny zestaw zależności, w tym przypadku spring-boot-starter-data-jpa. Skopiował bym utworząną przeze mnie encję oraz niezbędne encje zależne. Stworzyłbym testy jednostkowe „dla encji”. Następnie sukcesywnie dodawał bym kolejne elementy zależne, np.: repository, service.

Może, to się wydawać stratą czasu, ale w dłuższej perspektywie, tak nie jest. Mając na uwadze powyższe, zakładam, że w trakcie tworzenia nowego projektu zgłębiam wiedzę na temat Hibernate, JUnit, Spring Context. Rzadko kiedy mamy szansę stworzyć projekt od nowa, dodawać w nim początkowe elementy. W większości przypadków „lądujemy” w istniejącym projekcie.

Dlaczego IntelliJ w wersji Community? Zacznę od mojego ulubionego zdania w tym temacie, IntelliJ Ultimate rozleniwia i jest nadgorliwy – za dużo rzeczy robi za nas i za dużo podpowiada. Na etapie Junior i przejścia na Mid-Level Java Developer zalecam moim uczniom stosowanie IntelliJ Community. Jeżeli mamy już sporo doświadczenia i wiedzę na temat frameworków i stosowanych technologii, to IntelliJ Ultimate bardzo pomaga. Chociaż samemu cały czas używam IntelliJ w wersji Community.

Zadania na https://www.codewars.com/odskocznia od „nadmiaru kodu” – warstwy, loggery, frameworki – czysty, jednolinijkowy kod źródłowy. To tutaj możemy zweryfikować nasze umiejętności programistyczne, sprawdzić, co mamy jeszcze do poprawy, a co do nauki od zera. Bez goniących nas terminów w projekcie i masy codziennych rozmów z programistami i osobami zlecającymi napisanie kodu źródłowego.

Wyzwania na https://challengerocket.com/ChallengeRocket offers a new formula to hire outstanding candidates with IT, analytical, tech and financial skills. W tym miejscu możemy znaleźć zatrudnienie realizując wyzwania programistyczne. Weryfikując i ćwicząc nasze umiejętności programistyczne na prawdziwych zadaniach, a nie na CRUD z formularzem HTML.

„Get Trained For The Future, Today”https://careerkarma.com/ – Career Karma gives you the information, tools and support to figure out the skills you need today that will get you the job you want in the future.


Podsumowując, nie można poruszać się tylko po powierzchni zagadnień związanych z wykorzystywanymi frameowrkami i technologiami. Dobry programista, programistka dobrze znają narzędzia, których używają. Moim zdaniem przejście z Junior na Mid-Level Java Developer nie powinno być „nominowane z urzędu, ze względu na ilość przepracowanych lat”. Powinno następować w „naturalny sposób” wynikający z ilości oraz różnorodności projektów, w których uczestniczyła dana osoba.

Zdjęcie autorstwa Nataliya Vaitkevich z Pexels.