Proces tworzenia aplikacji biznesowych przypomina linię produkcyjną, na której każda partia przechodzi te same bramki jakości. Różnica polega na tym, że wytwarzany jest kod, a bramką jest test albo akceptacja użytkownika. Poniżej opisany jest cykl wytwarzania od wymagań po wydanie, ze szczególnym naciskiem na dwa obszary, które najczęściej decydują o stabilności po wdrożeniu: testy i wersjonowanie.
Tekst dotyczy aplikacji internetowych opartych na C# i SQL Server, czyli stosu, w którym SoftwareStudio wytwarza własne systemy. Ogólne zasady stosują się też do aplikacji mobilnych na Androida, choć dochodzi w nich dystrybucja pakietów.
Proces tworzenia aplikacji biznesowych - cykl wytwarzania
Cykl wytwarzania składa się z powtarzalnych faz, które w każdej iteracji dotyczą niewielkiego kawałka funkcjonalności. Krótka iteracja daje użytkownikom szybką informację zwrotną, a zespołowi wczesne wykrycie błędnych założeń. Dłuższe iteracje mają sens tylko tam, gdzie wdrożenie na środowisko testowe jest kosztowne.
| Faza cyklu | Co się w niej dzieje | Artefakt |
|---|---|---|
| Planowanie iteracji | Wybór pozycji z listy wymagań, oszacowanie pracochłonności | Zakres iteracji |
| Implementacja | Kodowanie na gałęzi funkcjonalnej, testy jednostkowe | Pull request |
| Przegląd kodu | Czytanie zmian przez drugą osobę i poprawki po uwagach | Zatwierdzona zmiana |
| Integracja | Scalenie z gałęzią główną oraz automatyczny build z testami | Numer builda |
| Demonstracja | Pokaz użytkownikom na środowisku testowym | Lista uwag |
| Wydanie | Wdrożenie na produkcję według planu, notatki do wydania | Oznaczona wersja |
Reguła: zmiana trafia na gałąź główną dopiero po przeglądzie i po zielonym wyniku automatycznych testów.
Zespół, który stosuje tę regułę, ma zawsze budowalną wersję gałęzi głównej. To upraszcza wydania, bo wydanie polega na oznaczeniu istniejącego stanu, a nie na sklejaniu zmian na ostatnią chwilę.
Szacowanie i planowanie iteracji
Pracochłonność szacuje się w godzinach, podając zakres zamiast jednej liczby, bo niepewność jest największa przy pozycjach dotykających integracji i bazy danych. Tempo zespołu mierzy się z poprzednich iteracji, czyli sumą godzin pozycji zamkniętych zgodnie z definicją ukończenia. Na tej podstawie planuje się kolejną iterację, a nie na podstawie życzeń co do terminu.
Pozycje, których nie da się oszacować, dostają najpierw krótkie badanie techniczne z ustalonym limitem czasu. Wynik badania może być prototypem albo notatką z opisem ryzyka. Dopiero po nim pozycja wraca na listę prac z oszacowaniem.
Wymagania jako lista prac i definicja ukończenia
Wymagania trafiają do listy prac, w której każda pozycja ma opis z priorytetem oraz kryterium akceptacji. Pozycja jest zamknięta wtedy, gdy spełnia definicję ukończenia uzgodnioną z zamawiającym na początku projektu. Bez takiej definicji „gotowe” znaczy co innego dla programisty i dla użytkownika.
- Kod - zmiana przeszła przegląd i została scalona z gałęzią główną, a build kończy się powodzeniem.
- Testy - funkcja ma testy automatyczne, a scenariusz akceptacyjny został wykonany na środowisku testowym.
- Baza danych - skrypt migracji jest dołączony do zmiany i przeszedł próbę na kopii bazy.
- Dokumentacja - zmiana jest opisana w notatkach do wydania, a jeśli dotyczy konfiguracji, także w instrukcji.

Sposób doprecyzowania zakresu przed startem opisuje tekst o analizie przedwdrożeniowej. Pozycje z listy prac powinny wynikać z tej analizy, a nie z pojedynczych rozmów z użytkownikami.
Repozytorium i przegląd kodu
Kod źródłowy leży w repozytorium z historią zmian, najczęściej w Git. Gałąź główna zawiera stan gotowy do wdrożenia, a każda funkcja powstaje na osobnej gałęzi krótkiego życia. Krótka gałąź ogranicza konflikty przy scalaniu, bo różnica względem głównej jest niewielka.
git checkout -b funkcja/uwagi-do-zamowienia
git commit -m "Dodano pole uwag do zamowienia"
git push origin funkcja/uwagi-do-zamowienia
# po przeglądzie i zielonym buildzie:
git checkout main
git merge --no-ff funkcja/uwagi-do-zamowienia
git tag v1.4.0
Znacznik na końcu oznacza wydanie. Dzięki niemu każdą wersję działającą u klienta da się odtworzyć z repozytorium, co jest warunkiem szukania błędu zgłoszonego na produkcji.
Automatyczny build po każdym scaleniu
Serwer buildów kompiluje aplikację i uruchamia testy po każdym scaleniu, a wynik jest widoczny przy zmianie. Build jest powtarzalny, bo wykonuje się w czystym środowisku według skryptu zapisanego w repozytorium, a nie na komputerze programisty. Efektem jest pakiet oznaczony numerem builda, który przechodzi kolejne środowiska bez ponownej kompilacji.
Co sprawdza przegląd kodu
Przegląd nie ma zastępować testów. Recenzent szuka rzeczy, których test nie złapie: zapytań wykonywanych w pętli, braku obsługi wyjątków, wartości wpisanych na stałe zamiast konfiguracji i zmian uprawnień. Z punktu widzenia bazy danych szczególnie ważny jest przegląd skryptów migracji, bo błąd w nich trudniej cofnąć niż błąd w kodzie aplikacji.
Ta praktyka wiąże się z tematem opisanym w tekście o programowaniu baz danych: zmiany schematu podlegają tym samym regułom przeglądu co kod.
Poziomy testów aplikacji biznesowej
Testy dzieli się według tego, jak duży fragment systemu obejmują. Im niższy poziom, tym test jest szybszy i tańszy w utrzymaniu, więc tych testów powinno być najwięcej. Testy wyższego poziomu są wolniejsze, ale sprawdzają współpracę elementów, której testy niższego poziomu nie widzą.
| Poziom | Co sprawdza | Kto wykonuje | Kiedy |
|---|---|---|---|
| Jednostkowy | Pojedynczą metodę, na przykład naliczanie kwoty brutto | Programista | Przy każdym buildzie |
| Integracyjny | Współpracę kodu z bazą danych lub interfejsem sąsiedniego systemu | Programista i tester | Przy każdym buildzie lub co noc |
| Akceptacyjny | Scenariusz biznesowy od początku do końca według kryteriów | Użytkownik końcowy | Przed wydaniem |
| Regresji | Czy zmiana nie zepsuła funkcji, które działały wcześniej | Zestaw automatyczny i tester | Przed każdym wydaniem |
Dane testowe i środowisko testowe
Testy akceptacyjne wymagają realistycznych danych. Najprościej wziąć kopię bazy produkcyjnej, ale dane osobowe nie powinny trafić do środowiska testowego bez zanonimizowania, bo dostęp do niego ma szersze grono osób. Skrypt anonimizacji jest częścią procesu odtwarzania środowiska i uruchamia się po każdym przywróceniu kopii.
UPDATE dbo.Kontrahenci
SET Email = CONCAT('kontrahent', Id, '@example.test'),
Telefon = NULL;

Poniższy przykład pokazuje test jednostkowy w xUnit. Kwoty przechowuje w groszach jako liczby całkowite, co unika błędów zaokrągleń typowych dla liczb zmiennoprzecinkowych. Nazwy klas i metod są przykładowe.
public static class Kalkulator
{
public static int Brutto(int nettoGrosze, int stawkaVat) =>
(int)Math.Round(nettoGrosze * (100 + stawkaVat) / 100.0,
MidpointRounding.AwayFromZero);
}
public class KalkulatorTests
{
[Theory]
[InlineData(10000, 23, 12300)]
[InlineData(1999, 23, 2459)]
public void Brutto_zaokragla_do_groszy(int netto, int stawka, int oczekiwane)
{
Assert.Equal(oczekiwane, Kalkulator.Brutto(netto, stawka));
}
}
Wprowadzenie do pisania takich testów w środowisku .NET zawiera dokumentacja Microsoft Learn o testach jednostkowych w .NET. Testy integracyjne z interfejsami sąsiednich systemów omawia tekst o integrowaniu aplikacji webowych. Testy akceptacyjne pisze się z użytkownikami, ponieważ to oni wiedzą, jaki wynik jest poprawny dla danego dokumentu.
Reguła: błąd zgłoszony z produkcji dostaje najpierw test, który go odtwarza, a dopiero potem poprawkę.
Test odtwarzający błąd zostaje w zestawie regresji. Po roku prowadzenia takiej praktyki zestaw zawiera historię wszystkich błędów, które kiedykolwiek się pojawiły, i pilnuje, żeby nie wróciły.
Testy wydajności i bezpieczeństwa
Testy obciążeniowe sprawdzają, jak aplikacja zachowuje się przy liczbie jednoczesnych użytkowników zbliżonej do rzeczywistej. Mierzy się czas odpowiedzi ekranów i zapytań, a wolne zapytania analizuje pod kątem indeksów i planu wykonania. Do porównania zapytań między wersjami służy Query Store, opisany w dokumentacji Microsoft Learn.
Testy bezpieczeństwa obejmują przede wszystkim kontrolę uprawnień, czyli sprawdzenie, czy użytkownik bez roli nie otworzy cudzego dokumentu przez zmianę adresu. Drugi obszar to ochrona przed wstrzyknięciem SQL. Zapytania z parametrami zamiast sklejania tekstu rozwiązują większość problemów.
using var cmd = new SqlCommand(
"SELECT Id, Nazwa FROM dbo.Kontrahenci WHERE NIP = @nip", conn);
cmd.Parameters.Add("@nip", SqlDbType.NVarChar, 20).Value = nip;
Każde miejsce, w którym aplikacja buduje zapytanie z tekstu podanego przez użytkownika, wychwytuje się podczas przeglądu kodu. Reguła jest prosta: wartość z formularza trafia do parametru, a nie do treści zapytania.
Wersjonowanie aplikacji i bazy danych
Numer wersji informuje, jakiego rodzaju zmiana zaszła. Najczęściej stosuje się schemat trzech liczb rozdzielonych kropkami, opisany w specyfikacji wersjonowania semantycznego. Pierwsza liczba rośnie przy zmianie niezgodnej wstecz, druga przy nowej funkcji, trzecia przy samej poprawce błędu.
| Rodzaj zmiany | Zmienia się | Przykład |
|---|---|---|
| Poprawka błędu bez zmiany działania | Trzecia liczba | 1.4.0 na 1.4.1 |
| Nowa funkcja zgodna wstecz | Druga liczba | 1.4.1 na 1.5.0 |
| Zmiana niezgodna z poprzednim interfejsem | Pierwsza liczba | 1.5.0 na 2.0.0 |
Wersja schematu bazy danych
Baza danych ma własną historię zmian, niezależną od numeru aplikacji. Każdy skrypt migracji zapisuje w tabeli wersji, że został wykonany, dzięki czemu ten sam skrypt uruchomiony drugi raz niczego nie zmienia. Poniższy przykład w T-SQL dodaje pole i odnotowuje wersję schematu w jednej transakcji, a nazwy tabel są przykładowe.
CREATE TABLE dbo.WersjaSchematu (
Wersja INT NOT NULL PRIMARY KEY,
Opis NVARCHAR(200) NOT NULL,
Wdrozono DATETIME2(0) NOT NULL DEFAULT SYSDATETIME()
);
SET XACT_ABORT ON;
IF NOT EXISTS (SELECT 1 FROM dbo.WersjaSchematu WHERE Wersja = 12)
BEGIN
BEGIN TRANSACTION;
ALTER TABLE dbo.Zamowienia ADD Uwagi NVARCHAR(500) NULL;
INSERT dbo.WersjaSchematu (Wersja, Opis)
VALUES (12, N'Uwagi do zamówienia');
COMMIT TRANSACTION;
END;
Nowa kolumna dopuszcza wartości NULL, więc stara wersja aplikacji nadal działa na zmienionej bazie. Ten wzorzec pozwala wdrażać bazę przed aplikacją i zmniejsza ryzyko przerwy w pracy użytkowników. Zmiany usuwające kolumny wprowadza się dopiero po wycofaniu wszystkich wersji, które ich używają.
Środowiska i wydania
Ten sam pakiet wdraża się kolejno na środowisko testowe i produkcyjne. Zmienia się tylko konfiguracja, taka jak łańcuch połączenia i adresy integracji. Dzięki temu na produkcji działa dokładnie to, co przeszło testy akceptacyjne, a nie ponowny build z innego stanu repozytorium.
Do każdego wydania dołącza się notatki opisujące nowe funkcje i poprawione błędy oraz wymagane kroki konfiguracyjne. Użytkownicy dostają z nich informację, co się zmieniło, a dział wsparcia dostaje listę rzeczy do sprawdzenia po wdrożeniu. Praktykę tę wspiera opis z tekstu o aplikacji biznesowej jako module systemu IT.

Wycofanie wydania
Wycofanie aplikacji jest proste, bo wystarczy wdrożyć poprzedni pakiet. Wycofanie bazy jest trudne, jeśli w międzyczasie zapisano nowe dane. Z tego powodu migracje planuje się jako zmiany zgodne wstecz, a przed wydaniem wykonuje się kopię bazy z potwierdzoną możliwością odtworzenia.
- Zmiana zgodna wstecz - stara wersja aplikacji działa na nowym schemacie, więc wycofanie ogranicza się do pakietu aplikacji.
- Zmiana niezgodna wstecz - wymaga osobnego planu, kopii bazy przed wydaniem i decyzji, do kiedy wycofanie jest jeszcze możliwe.
Poprawki po wdrożeniu i utrzymanie wersji
Po wdrożeniu praca nie kończy się, bo użytkownicy zgłaszają uwagi. Zmieniają się także przepisy i procesy. Poprawkę pilną, na przykład błąd blokujący wystawianie dokumentów, wprowadza się na osobnej gałęzi wydania i dostarcza jako zwiększenie trzeciej liczby. Zmiany zwykłe czekają na najbliższe planowe wydanie.
| Rodzaj zgłoszenia | Ścieżka w repozytorium | Termin wdrożenia |
|---|---|---|
| Błąd blokujący pracę | Gałąź wydania i zwiększenie trzeciej liczby | Poza cyklem, po teście regresji |
| Błąd zwykły | Gałąź funkcjonalna i przegląd kodu | Najbliższe planowe wydanie |
| Zmiana wymagań | Nowa pozycja na liście prac | Iteracja wskazana przez zamawiającego |
Koszty tego okresu opisuje tekst o tworzeniu programów dla firm, a zasady doboru wykonawcy i zakres oferty przedstawia strona producenta aplikacji biznesowych. Pełną listę zagadnień programowania takich aplikacji zawiera artykuł o programowaniu aplikacji biznesowych.
Przy wielu wdrożeniach tej samej aplikacji pojawia się pytanie, ile wersji utrzymuje się równolegle. Rozsądna zasada mówi, że poprawki bezpieczeństwa trafiają do wersji aktualnej i poprzedniej, a starsze wymagają aktualizacji. Zasadę spisuje się w umowie, aby zamawiający wiedział, kiedy wersja wychodzi z okresu wsparcia.
Reguła: wersja poza okresem wsparcia nie dostaje poprawek bezpieczeństwa, więc umowa podaje datę końca wsparcia dla każdego wydania.
Aplikacje webowe udostępniane przez przeglądarkę wymagają jeszcze planu wsparcia przeglądarek. Wykonawca ustala z zamawiającym listę wersji, na których program jest testowany, a każde odstępstwo zapisuje w notatkach do wydania. Więcej o projektowaniu warstwy interfejsu jest w tekście o projektowaniu aplikacji webowych, a ofertę na zamówienie można sprawdzić na stronie usług SoftwareStudio.



