Learning Log #5: Pierwszy większy projekt w Java – od pomysłu do działającej aplikacji

with Brak komentarzy
pierwszy projekt w Java

Learning Log #5: Pierwszy większy projekt w Java – od pomysłu do działającej aplikacji

Wtorek, 3 czerwca, 22:47. Ostatni test przechodzi. Zielone checkmarki w konsoli. Aplikacja działa.

Mój pierwszy większy projekt w Java jest GOTOWY.

Patrzę na ekran i czuję… dumę? Ulgę? Niedowierzanie? Wszystko naraz.

5 tygodni temu to był tylko pomysł w Notion: „Zbuduj coś prawdziwego, nie tylko ćwiczenia z kursu.” Dzisiaj mam 47 klas, 2847 linii kodu i działającą aplikację do zarządzania terapiami mojego dziecka.

Pierwszy projekt w Java to przełom. To moment, gdy przestajesz być „osobą, która się uczy” i stajesz się „osobą, która buduje”. To jest ogromna różnica.

Dziś opowiem Wam całą historię – od pomysłu, przez architekturę, wyzwania techniczne, do finału. I najważniejsze: co czułam kończąc pierwszy prawdziwy projekt jako programistka.

Skąd pomysł – dlaczego akurat aplikacja do terapii?

Problem, który widziałam codziennie

Moja rzeczywistość:

  • 8-9 godzin terapii tygodniowo (SI, logopedia, pedagogika, TUS)
  • 3 różne ośrodki
  • 5 terapeutów
  • Niekończące się faktury, zestawienia kosztów, raporty dla NFZ
  • Excel, który się sypie co miesiąc

Próbowałam gotowych rozwiązań:

  • Kalendarz Google – za prosty (tylko daty, bez kosztów)
  • Excel – chaotyczny, gubię dane
  • Aplikacje do zarządzania zdrowiem – zbyt ogólne, nie dla terapii

Pomyślałam: „Co jeśli zbuduję własną aplikację?”

Punkt zwrotny – rozmowa z instruktorem kursu

Napisałam na Discord Zajavka:

„Skończyłam podstawy kursu. Co dalej? Spring? JavaScript?”

Instruktor odpowiedział:

„Przed Springiem zbuduj JEDEN solidny projekt w czystej Javie. Coś co rozwiązuje prawdziwy problem. Pokaże Ci jak projektować aplikacje, nie tylko pisać kod.”

I kliknęło.

Pierwszy projekt w Java – planowanie

Tydzień 1: Research i wymyślanie funkcjonalności

Pytania, które sobie zadałam:

  1. Co aplikacja MUSI umieć? (MVP – Minimum Viable Product)
    • Dodawanie sesji terapii
    • Przechowywanie informacji o terapeutach i ośrodkach
    • Kalkulacja kosztów miesięcznych
    • Generowanie raportów
  2. Co byłoby FAJNE, ale nie konieczne? (v2.0)
    • Przypomnienia o sesjach
    • Statystyki roczne
    • Export do PDF
    • Integracja z kalendarzem
  3. Czego NIE potrzebuję? (scope control)
    • GUI (graficzny interfejs) – na razie konsola wystarczy
    • Baza danych – na razie pliki tekstowe
    • Multi-user – tylko ja używam

Decyzja: Buduję wersję konsolową z podstawowymi funkcjami. GUI później.

Tydzień 2: Architektura – najpierw plan, potem kod

Błąd początkujących: Otwieram IDE i zaczynam pisać kod bez planu.

Lepsze podejście: Planuję strukturę na papierze.

Moja architektura – diagram klas:

MAIN ENTITIES (główne encje):

TherapySession (Sesja terapii)
├── sessionId: String
├── therapyType: TherapyType (enum)
├── therapist: Therapist
├── dateTime: LocalDateTime
├── duration: int (minuty)
├── cost: BigDecimal
├── paid: boolean
├── notes: String

Therapist (Terapeuta)
├── therapistId: String
├── name: String
├── specialization: String
├── phone: String
├── email: String
├── hourlyRate: BigDecimal

TherapyCenter (Ośrodek terapeutyczny)
├── centerId: String
├── name: String
├── address: Address
├── phone: String
├── therapists: List<Therapist>

TherapyType (Typ terapii - ENUM)
├── SI (Integracja sensoryczna)
├── SPEECH (Logopedia)
├── PEDAGOGY (Pedagogika)
├── EARLY_INTERVENTION (Wczesne wspomaganie)

SERVICES (serwisy – logika biznesowa):

TherapySessionService
├── addSession()
├── updateSession()
├── deleteSession()
├── findSessionById()
├── getAllSessions()
├── getSessionsByMonth()
├── getSessionsByTherapist()

ReportService
├── generateMonthlyReport()
├── calculateTotalCost()
├── calculateCostByType()
├── generateYearlyStatistics()

PaymentService
├── markAsPaid()
├── getUnpaidSessions()
├── getTotalUnpaid()

REPOSITORIES (dostęp do danych):

FileRepository<T>
├── save(T entity)
├── findById(String id)
├── findAll()
├── delete(String id)
├── update(T entity)

UTILITIES (narzędzia pomocnicze):

DateUtils - formatowanie dat
ValidationUtils - walidacja danych
FileUtils - operacje na plikach

Dlaczego taka struktura?

Podział odpowiedzialności:

  • Entities = Dane (co przechowujemy)
  • Services = Logika (co robimy z danymi)
  • Repositories = Dostęp (jak zapisujemy/odczytujemy)
  • UI = Interfejs (jak użytkownik widzi)

Efekt: Jeśli zmienię sposób zapisywania (z plików na bazę danych) – zmieniam tylko Repository. Services i Entities pozostają bez zmian.

Tydzień 3-4: Implementacja – pierwszy kod

Zaczynanie od Entity

TherapySession.java – pierwsza klasa:

java

public class TherapySession {
    private String sessionId;
    private TherapyType therapyType;
    private Therapist therapist;
    private LocalDateTime dateTime;
    private int durationMinutes;
    private BigDecimal cost;
    private boolean paid;
    private String notes;
    
    // Constructor
    public TherapySession(String sessionId, TherapyType therapyType, 
                         Therapist therapist, LocalDateTime dateTime,
                         int durationMinutes, BigDecimal cost) {
        this.sessionId = sessionId;
        this.therapyType = therapyType;
        this.therapist = therapist;
        this.dateTime = dateTime;
        this.durationMinutes = durationMinutes;
        this.cost = cost;
        this.paid = false; // domyślnie nieopłacone
    }
    
    // Getters and setters
    public String getSessionId() { return sessionId; }
    public TherapyType getTherapyType() { return therapyType; }
    // ... itd
    
    // Business methods
    public void markAsPaid() {
        this.paid = true;
    }
    
    public boolean isInMonth(int year, int month) {
        return dateTime.getYear() == year 
            && dateTime.getMonthValue() == month;
    }
    
    @Override
    public String toString() {
        return String.format("%s | %s | %s | %.2f zł | %s",
            dateTime.format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")),
            therapyType,
            therapist.getName(),
            cost,
            paid ? "OPŁACONE" : "DO ZAPŁATY"
        );
    }
}

Co się nauczyłam:

  • BigDecimal dla pieniędzy (nie double! – problemy z precyzją)
  • LocalDateTime dla dat (lepsze niż Date)
  • Enum dla typów terapii (type-safe, nie String)
  • Business methods w encji (markAsPaid, isInMonth)

Wzorzec repozytorium – pierwsza abstrakcja

Problem: Jak zapisywać dane?

Opcje:

  1. Baza danych (za trudne na start)
  2. Pliki tekstowe (proste, ale jak czytać/zapisywać?)

Rozwiązanie: Wzorzec repozytorium z plikami JSON

FileRepository.java – generyczny repozytorium:

java

public class FileRepository<T> {
    private final String filePath;
    private final Class<T> entityClass;
    private final ObjectMapper objectMapper; // Jackson dla JSON
    
    public FileRepository(String filePath, Class<T> entityClass) {
        this.filePath = filePath;
        this.entityClass = entityClass;
        this.objectMapper = new ObjectMapper()
            .registerModule(new JavaTimeModule()); // dla LocalDateTime
    }
    
    public void save(T entity) throws IOException {
        List<T> entities = findAll();
        entities.add(entity);
        writeToFile(entities);
    }
    
    public List<T> findAll() throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            return new ArrayList<>();
        }
        
        return objectMapper.readValue(file, 
            objectMapper.getTypeFactory()
                .constructCollectionType(List.class, entityClass)
        );
    }
    
    public Optional<T> findById(String id) throws IOException {
        return findAll().stream()
            .filter(e -> getId(e).equals(id))
            .findFirst();
    }
    
    public void update(T entity) throws IOException {
        List<T> entities = findAll();
        String id = getId(entity);
        
        entities.removeIf(e -> getId(e).equals(id));
        entities.add(entity);
        writeToFile(entities);
    }
    
    public void delete(String id) throws IOException {
        List<T> entities = findAll();
        entities.removeIf(e -> getId(e).equals(id));
        writeToFile(entities);
    }
    
    private void writeToFile(List<T> entities) throws IOException {
        objectMapper.writerWithDefaultPrettyPrinter()
            .writeValue(new File(filePath), entities);
    }
    
    // Reflection do wyciągnięcia ID
    private String getId(T entity) {
        try {
            Method method = entity.getClass().getMethod("getSessionId");
            return (String) method.invoke(entity);
        } catch (Exception e) {
            throw new RuntimeException("Entity must have getSessionId method", e);
        }
    }
}

Co się nauczyłam:

  • Generics (<T>) – jeden magazyn dla różnych encji
  • Jackson ObjectMapper – serialization (proces przekształcania obiektów, tj. instancji określonych klas) do JSON
  • Reflection (mechanizm, który umożliwia na odczyt i modyfikację informacji o klasach) – wyciąganie ID dynamicznie
  • Exception handling (Obsługa wyjątków )- IOException musi być obsłużone

Service layer – logika biznesowa

TherapySessionService.java:

java

public class TherapySessionService {
    private final FileRepository<TherapySession> repository;
    
    public TherapySessionService() {
        this.repository = new FileRepository<>(
            "data/therapy_sessions.json", 
            TherapySession.class
        );
    }
    
    public void addSession(TherapySession session) {
        try {
            // Walidacja
            if (session.getCost().compareTo(BigDecimal.ZERO) <= 0) {
                throw new IllegalArgumentException("Koszt musi być > 0");
            }
            if (session.getDateTime().isAfter(LocalDateTime.now())) {
                System.out.println("UWAGA: Sesja w przyszłości");
            }
            
            repository.save(session);
            System.out.println("✓ Dodano sesję: " + session);
            
        } catch (IOException e) {
            System.err.println("Błąd zapisu: " + e.getMessage());
        }
    }
    
    public List<TherapySession> getSessionsByMonth(int year, int month) {
        try {
            return repository.findAll().stream()
                .filter(s -> s.isInMonth(year, month))
                .sorted(Comparator.comparing(TherapySession::getDateTime))
                .toList();
        } catch (IOException e) {
            System.err.println("Błąd odczytu: " + e.getMessage());
            return new ArrayList<>();
        }
    }
    
    public void markSessionAsPaid(String sessionId) {
        try {
            Optional<TherapySession> session = repository.findById(sessionId);
            if (session.isPresent()) {
                session.get().markAsPaid();
                repository.update(session.get());
                System.out.println("✓ Oznaczono jako opłacone");
            } else {
                System.out.println("✗ Nie znaleziono sesji");
            }
        } catch (IOException e) {
            System.err.println("Błąd: " + e.getMessage());
        }
    }
}

Co się nauczyłam:

  • Walidacja danych (sprawdzanie przed zapisem)
  • User feedback (komunikaty co się dzieje)
  • Error handling (try-catch, komunikaty błędów)
  • Stream API dla filtrowania i sortowania

Report Service – generowanie raportów

To była najtrudniejsza część.

ReportService.java:

java

public class ReportService {
    private final TherapySessionService sessionService;
    
    public void generateMonthlyReport(int year, int month) {
        List<TherapySession> sessions = 
            sessionService.getSessionsByMonth(year, month);
        
        if (sessions.isEmpty()) {
            System.out.println("Brak sesji w tym miesiącu");
            return;
        }
        
        System.out.println("=".repeat(80));
        System.out.println("RAPORT MIESIĘCZNY: " + month + "/" + year);
        System.out.println("=".repeat(80));
        
        // Grupowanie po typie terapii
        Map<TherapyType, List<TherapySession>> byType = sessions.stream()
            .collect(Collectors.groupingBy(TherapySession::getTherapyType));
        
        // Podsumowanie per typ
        byType.forEach((type, typeSessions) -> {
            int count = typeSessions.size();
            int totalMinutes = typeSessions.stream()
                .mapToInt(TherapySession::getDurationMinutes)
                .sum();
            BigDecimal totalCost = typeSessions.stream()
                .map(TherapySession::getCost)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            long paidCount = typeSessions.stream()
                .filter(TherapySession::isPaid)
                .count();
            
            System.out.printf("\n%s:\n", type);
            System.out.printf("  Liczba sesji: %d\n", count);
            System.out.printf("  Całkowity czas: %dh %dmin\n", 
                totalMinutes / 60, totalMinutes % 60);
            System.out.printf("  Całkowity koszt: %.2f zł\n", totalCost);
            System.out.printf("  Opłacone: %d/%d\n", paidCount, count);
        });
        
        // Suma totalna
        BigDecimal grandTotal = sessions.stream()
            .map(TherapySession::getCost)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        BigDecimal unpaid = sessions.stream()
            .filter(s -> !s.isPaid())
            .map(TherapySession::getCost)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        System.out.println("\n" + "=".repeat(80));
        System.out.printf("SUMA CAŁKOWITA: %.2f zł\n", grandTotal);
        System.out.printf("DO ZAPŁATY: %.2f zł\n", unpaid);
        System.out.println("=".repeat(80));
    }
}
```

**Output przykładowy:**
```
================================================================================
RAPORT MIESIĘCZNY: 5/2026
================================================================================

SI:
  Liczba sesji: 8
  Całkowity czas: 8h 0min
  Całkowity koszt: 600.00 zł
  Opłacone: 8/8

SPEECH:
  Liczba sesji: 8
  Całkowity czas: 6h 0min
  Całkowity koszt: 520.00 zł
  Opłacone: 6/8

PEDAGOGY:
  Liczba sesji: 4
  Całkowity czas: 4h 0min
  Całkowity koszt: 240.00 zł
  Opłacone: 4/4

================================================================================
SUMA CAŁKOWITA: 1360.00 zł
DO ZAPŁATY: 130.00 zł
================================================================================

Co się nauczyłam:

  • Collectors.groupingBy() – grupowanie strumieni
  • Reduce dla sumowania BigDecimal
  • String formatting (printf)
  • Prezentacja danych (czytelny output)

Tydzień 5: UI i finiszowanie

Interfejs konsolowy – prosty, ale funkcjonalny

MainMenu.java:

java

public class MainMenu {
    private final Scanner scanner;
    private final TherapySessionService sessionService;
    private final ReportService reportService;
    
    public void run() {
        while (true) {
            displayMenu();
            int choice = getChoice();
            
            switch (choice) {
                case 1 -> addSession();
                case 2 -> listSessions();
                case 3 -> markAsPaid();
                case 4 -> generateReport();
                case 5 -> {
                    System.out.println("Do widzenia!");
                    return;
                }
                default -> System.out.println("Nieprawidłowy wybór");
            }
        }
    }
    
    private void displayMenu() {
        System.out.println("\n" + "=".repeat(50));
        System.out.println("SYSTEM ZARZĄDZANIA TERAPIAMI");
        System.out.println("=".repeat(50));
        System.out.println("1. Dodaj sesję terapii");
        System.out.println("2. Pokaż sesje (obecny miesiąc)");
        System.out.println("3. Oznacz jako opłacone");
        System.out.println("4. Generuj raport miesięczny");
        System.out.println("5. Wyjście");
        System.out.print("\nWybór: ");
    }
    
    private void addSession() {
        System.out.println("\n=== NOWA SESJA ===");
        
        // Wybór typu terapii
        System.out.println("Typ terapii:");
        TherapyType[] types = TherapyType.values();
        for (int i = 0; i < types.length; i++) {
            System.out.printf("%d. %s\n", i + 1, types[i]);
        }
        int typeChoice = scanner.nextInt() - 1;
        TherapyType type = types[typeChoice];
        
        // Wybór terapeuty (z listy wcześniej dodanych)
        Therapist therapist = selectTherapist();
        
        // Data i czas
        System.out.print("Data (dd.MM.yyyy): ");
        String dateStr = scanner.next();
        System.out.print("Godzina (HH:mm): ");
        String timeStr = scanner.next();
        LocalDateTime dateTime = parseDateTime(dateStr, timeStr);
        
        // Czas trwania
        System.out.print("Czas trwania (minuty): ");
        int duration = scanner.nextInt();
        
        // Koszt
        System.out.print("Koszt (zł): ");
        BigDecimal cost = scanner.nextBigDecimal();
        
        // Utworzenie sesji
        TherapySession session = new TherapySession(
            UUID.randomUUID().toString(),
            type,
            therapist,
            dateTime,
            duration,
            cost
        );
        
        sessionService.addSession(session);
    }
}

Co się nauczyłam:

  • Scanner dla input użytkownika
  • Menu loop (while true + switch)
  • UUID dla unikalnych ID
  • User experience (komunikaty, formatowanie)

Wyzwania techniczne – co było najtrudniejsze

1. Serializacja LocalDateTime do JSON

Problem:

java

Exception: Cannot serialize LocalDateTime

Rozwiązanie:

java

ObjectMapper mapper = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

Lekcja: Jackson potrzebuje dodatkowych modułów dla Java 8 Date/Time API.

2. BigDecimal arytmetyka

Problem:

java

BigDecimal total = cost1 + cost2; // BŁĄD! Nie można + dla BigDecimal

Rozwiązanie:

java

BigDecimal total = cost1.add(cost2);

Lekcja: BigDecimal to immutable (niemodyfikowalny), używaj metod (.add, .subtract, .multiply)

3. File I/O exceptions

Problem: Aplikacja crashuje gdy plik nie istnieje.

Rozwiązanie:

java

public List<T> findAll() throws IOException {
    File file = new File(filePath);
    if (!file.exists()) {
        // Utwórz pusty plik
        file.getParentFile().mkdirs();
        file.createNewFile();
        writeToFile(new ArrayList<>());
        return new ArrayList<>();
    }
    return objectMapper.readValue(file, ...);
}

Lekcja: Zawsze sprawdzaj czy plik/folder istnieje przed czytaniem.

4. Null handling w Optional

Problem:

java

Rozwiązanie:

java

Optional<TherapySession> sessionOpt = repository.findById(id);
if (sessionOpt.isPresent()) {
    sessionOpt.get().markAsPaid();
} else {
    System.out.println("Nie znaleziono");
}

// Lub functional style:
sessionOpt.ifPresent(TherapySession::markAsPaid);
```

**Lekcja:** Optional to explicite "może być null", obsługuj zawsze.

## Statystyki projektu - liczby

**Czas pracy:** 5 tygodni (kwiecień-maj 2026)
- Tydzień 1: Planowanie (10h)
- Tydzień 2: Architektura (8h)
- Tydzień 3-4: Implementacja (25h)
- Tydzień 5: UI + testy (12h)
- **Total: ~55 godzin**

**Kod:**
- **47 klas** (entities, services, repositories, utils, UI)
- **2847 linii kodu** (bez komentarzy)
- **89 metod** (średnio 32 linie/klasa)
- **23 pliki testowe** (JUnit tests)

**Biblioteki użyte:**
- Jackson (JSON serialization)
- JUnit 5 (testy jednostkowe)
- SLF4J + Logback (logging)

**Struktura folderów:**
```
therapy-manager/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   ├── com.therapy/
│   │   │   │   ├── entity/
│   │   │   │   ├── service/
│   │   │   │   ├── repository/
│   │   │   │   ├── util/
│   │   │   │   └── ui/
│   │   └── resources/
│   └── test/
│       └── java/
├── data/ (JSON files)
├── pom.xml (Maven dependencies)
└── README.md

Co czułam finiszując projekt

3 czerwca, 22:47 – ostatni test przechodzi

Uczucia w tym momencie:

1. Niedowierzanie „To naprawdę działa? Ja to zbudowałam?”

5 miesięcy temu nie znałam Javy. Dzisiaj mam działającą aplikację z 47 klasami.

2. Duma Pokazałam partnerowi: „Patrz, to dodaje sesję, to generuje raport…”

On: „Zrobiłaś TO?! To jest niesamowite!”

Ja: wzruszenie (szczęśliwe łzy)

3. Ulga Mogłam odpuścić 100 razy. „Za trudne”, „Nie dam rady”, „Może później”.

Ale nie odpuściłam. I skończyłam.

4. Więcej… To był tylko początek. Już myślałam: „Co dalej? GUI? Baza danych? Spring?”

Perspektywa – co projekt zmienił

Przed projektem:

  • „Programowanie to nauka składni i algorytmów”
  • „Projekty to coś dla ekspertów”
  • „Ja tylko uczę się z tutoriali”

Po projekcie:

  • „Programowanie to rozwiązywanie problemów”
  • „Projekty to jedyny sposób, żeby naprawdę się nauczyć”
  • „Ja BUDUJĘ, nie tylko uczę się”

Mentalna zmiana: Od „osoby która się uczy” do „programistki”.

Co bym zrobiła inaczej

1. Testy od początku, nie na końcu

Zrobiłam: Napisałam kod, potem testy.

Lepiej: TDD (Test-Driven Development) – testy najpierw, kod potem.

Dlaczego: Łatwiej refaktorować, gdy masz testy.

2. Mniejsze commity do Git

Zrobiłam: Wielkie commity „Added 10 classes”

Lepiej: Małe, atomowe commity „Add TherapySession entity”, „Add FileRepository”

Dlaczego: Łatwiej wrócić do poprzedniej wersji.

3. Więcej loggingu

Zrobiłam: System.out.println() wszędzie

Lepiej: SLF4J logger z poziomami (DEBUG, INFO, ERROR)

Dlaczego: Łatwiejszy debugging na produkcji.

4. Dokumentacja w trakcie, nie po

Zrobiłam: Dokumentacja README na końcu (zapomniałam połowę decyzji)

Lepiej: Notatki w Notion podczas budowania

Dlaczego: Świeża pamięć = lepsza dokumentacja.

Co dalej – plany na projekt

Wersja 1.0 (zrobione):

  • Konsolowy interface
  • JSON file storage
  • Podstawowe raporty

Wersja 2.0 (czerwiec-lipiec):

  • GUI (JavaFX) – graficzny interfejs
  • Eksport raportów do PDF
  • Charts (wykresy kosztów)

Wersja 3.0 (sierpień-wrzesień):

  • Baza danych (PostgreSQL zamiast JSON)
  • Multi-user (mąż też może dodawać sesje)
  • Web interface (może Spring Boot?)

Ale najpierw: Przerwa. Odpoczynek. Perspektywa.

Nauki z pierwszego projektu w Java

Techniczne:

OOP design – jak projektować klasy i relacje
Wzorce projektowe – repozytorium, warstwa usług, wstrzykiwanie zależności
File I/O – czytanie/zapis plików, JSON serializacja
Kolekcje i strumienie – filtrowanie, grupowanie, redukcja
Obsługa wyjątków – try-catch, niestandardowe wyjątki
Testing – JUnit, pokrycie testowe

Nietechniczne (ważniejsze!):

Ukończenie > Perfekcja – lepiej skończony prosty projekt niż nie skończony perfekcyjny
Małe kroki – 1h dziennie przez 5 tygodni > 10h przez 1 dzień
Realne problemy – budowanie czegoś co rozwiązuje własny problem = motywacja
Liczy się pęd – 1 dzień przerwy OK, 3 dni = trudno wrócić
Świętuj zwycięstwa – skończony projekt = osiągnięcie, świętuj to!

FAQ – pierwszy projekt w Java

Czy potrzebuję skończyć cały kurs przed projektem?

NIE. Ja zaczęłam projekt po:

  • Podstawach składni (zmienne, pętle, warunki)
  • OOP (klasy, obiekty, dziedziczenie)
  • Collections (List, Map, Set)
  • Podstawach Streams

Reszta nauczyłam się w trakcie projektu (JSON, File I/O, design patterns).

Ile czasu dziennie pracowałaś?

Średnio: 1.5h dziennie (10-15h tygodniowo)

Rozłożenie:

  • Popołudnia: 1h (14:00-15:00)
  • Wieczory: 30 min (21:30-22:00) – gdy energia była

Weekendy: 3-4h (sobota rano, partner z dziećmi)

Jak wybrać dobry projekt dla początkujących?

3 kryteria:

  1. Rozwiązuje TWÓJ problem – motywacja wewnętrzna
  2. Zakres realistyczny – zrobisz w 4-6 tygodni
  3. Trochę za trudny – uczysz się nowego, ale nie przytłaczający

Przykłady dobrych pierwszych projektów:

  • Todo list manager (klasyk, ale działa)
  • Expense tracker (śledzenie wydatków)
  • Library manager (zarządzanie książkami)
  • Habit tracker (śledzenie nawyków)
  • Recipe organizer (przepisy kulinarne)

Zły projekt: „Zbuduję Facebooka” (gwarantowane rozszerzenie zakresu)

Co jeśli utknę i nie wiem jak coś zrobić?

Moja strategia:

1. Google (15 min) – „How to serialize LocalDateTime to JSON Java”
2. Stack Overflow (15 min) – czytam różne rozwiązania
3. ChatGPT (10 min) – pytam o konkretny problem
4. Discord/forum kursu (24h) – pytam instruktora
5. Następnego dnia świeży umysł– czasem sen rozwiązuje

Zasada: Max 1h szukania rozwiązania. Potem przerwa/pomoc.

Czy pokazać projekt w portfolio?

TAK! Nawet prosty projekt pokazuje:

  • Umiesz skończyć, co zaczynasz
  • Rozumiesz OOP i design patterns
  • Piszesz czysty kod
  • Rozwiązujesz problemy

Lepiej: 1 skończony prosty projekt niż 10 nie skończonych „ambitnych”.

Podsumowanie – mój pierwszy prawdziwy projekt

Therapy Manager v1.0:

  • 47 klas
  • 2847 linii kodu
  • 55 godzin pracy
  • 5 tygodni od pomysłu do finiszu
  • Aplikacja która rozwiązuje mój realny problem

Co zyskałam:

  • Umiejętność projektowania aplikacji
  • Pewność siebie jako programistka
  • Element portfolio (pokazuję na GitHubie)
  • Praktyczne narzędzie (używam codziennie!)

Najważniejsza lekcja:

„Programowania nie uczy się przez czytanie poradników. Uczy się przez BUDOWANIE.”

Możesz przeczytać 100 tutoriali o Javie. Ale dopiero gdy zbudujesz projekt – rozumiesz jak to wszystko się łączy.

Pierwszy projekt w Java to przełom. Od „osoby która się uczy” do „osoby która buduje”.

I jeśli ja mogłam – z godzinę dziennie, z dziećmi w terapiach, między freelancingiem – to Ty też możesz.

Zbuduj coś. Zakończ to. Świętuj.


PS: Też budujesz swój pierwszy projekt? Utknąłeś gdzieś? Napisz w komentarzu – chętnie pomogę! A jeśli skończyłeś projekt – podziel się, co zbudowałeś? Ciekawa jestem!

Zostaw Komentarz