Przejdź do głównej treści
Grafika przedstawia ukryty obrazek

Dług technologiczny w oprogramowaniu – wyjaśnienie i przykłady w PHP

Ilustracja tematu Dug technologiczny w oprogramowaniu  wyjanienie i przykady w PHP

Dług technologiczny (technical debt) to zjawisko, w którym programiści wprowadzają szybkie, kompromisowe rozwiązania, aby szybciej dostarczyć funkcjonalność kosztem jakości kodu, architektury lub testów. Na krótką metę daje to zysk czasowy, ale w przyszłości trzeba „spłacić dług”, czyli wykonać dodatkową pracę: refaktoryzację, poprawę struktury, usunięcie błędów.

Dlaczego powstaje dług technologiczny?

Najczęstsze źródła długu to:

  • presja biznesowa i krótkie terminy,
  • szybkie prototypowanie bez późniejszego porządkowania kodu,
  • brak testów automatycznych,
  • słaba architektura lub brak standardów,
  • brak wiedzy lub doświadczenia w momencie tworzenia kodu,
  • starzejące się technologie i biblioteki.

Skutki długu technologicznego

  • wolniejsze wdrażanie nowych funkcji,
  • większa liczba błędów,
  • coraz trudniejsza modyfikacja systemu,
  • frustracja zespołu,
  • wyższe koszty utrzymania.

Przykłady długu technologicznego w PHP

Poniżej znajdziesz najczęstsze typy długu oraz ich ilustracje w kodzie.

1. Zduplikowany kod (duplication)

Kod z długiem

Problem: ta sama logika (stawka VAT) powielona w kilku miejscach.

<?php

function priceWithVat($price) {
    return $price * 1.23; // VAT powtórzony
}

function discountedPrice($price) {
    $discounted = $price * 0.9;
    return $discounted * 1.23; // VAT powtórzony ponownie
}

Lepiej – jeden punkt prawdy

<?php

const VAT = 1.23;

function priceWithVat($price) {
    return $price * VAT;
}

function discountedPrice($price) {
    return ($price * 0.9) * VAT;
}

2. Zbyt duża funkcja („spaghetti code”)

Kod z długiem

Jedna metoda robi wszystko naraz: waliduje zamówienie, liczy cenę, wysyła maila.

<?php

function processOrder($order) {
    // Walidacja
    if (empty($order['items'])) {
        throw new Exception("Empty order");
    }
    if (empty($order['user']['email'])) {
        throw new Exception("Invalid user");
    }

    // Liczenie ceny
    $total = 0;
    foreach ($order['items'] as $item) {
        $total += $item['price'] * $item['qty'];
    }

    // Wysyłanie maila
    mail($order['user']['email'], "Order processed", "Total: $total");

    return $total;
}

Kod po refaktoryzacji

Separacja odpowiedzialności → łatwiejsze testowanie i utrzymanie.

<?php

function validateOrder($order) { /* ... */ }
function calculateTotal($order) { /* ... */ }
function notifyUser($user, $total) { /* ... */ }

function processOrder($order) {
    validateOrder($order);
    $total = calculateTotal($order);
    notifyUser($order['user'], $total);
    return $total;
}

3. Hard-coding – wartości na sztywno

Kod z długiem

Konfiguracja wpisana na stałe → trudna zmiana i testowanie.

<?php

class EmailClient {
    public function send($msg) {
        $smtp = "smtp.server.com"; // na sztywno
        echo "Connecting to $smtp\n";
    }
}

Kod po poprawie

Konfiguracja przekazywana z zewnątrz.

<?php

class EmailClient {
    private $smtp;

    public function __construct($smtp) {
        $this->smtp = $smtp;
    }

    public function send($msg) {
        echo "Connecting to {$this->smtp}\n";
    }
}

4. Brak obsługi błędów

Kod z długiem

Jeśli plik nie istnieje – fatal error.

<?php

function readFileContent($path) {
    return file_get_contents($path); // brak obsługi wyjątków
}

Kod po poprawie

Bezpieczna obsługa błędów.

<?php

function readFileContent($path) {
    if (!file_exists($path)) {
        return "";
    }

    return file_get_contents($path);
}

5. Brak testów automatycznych

Kod z długiem

Funkcja działa, ale brak testów powoduje wzrost ryzyka regresji.

<?php

function add($a, $b) {
    return $a + $b;
}

Kod z testem PHPUnit

<?php

use PHPUnit\Framework\TestCase;

class MathTest extends TestCase {
    public function testAdd() {
        $this->assertEquals(5, add(2, 3));
    }
}

Kompletny projekt PHP: Przykład długu i jego refaktoryzacji

Poniżej znajduje się prosty projekt PHP, który prezentuje kod z długiem technologicznym oraz jego poprawioną wersję.

Projekt składa się z trzech głównych plików:

  1. order_processor.php – logika przetwarzania zamówienia.
  2. email_client.php – wysyłanie wiadomości e-mail.
  3. index.php – uruchomienie przykładowego procesu.

Część I: Projekt z długiem technologicznym

order_processor.php (z długiem)

<?php

function processOrder($order) {
    // Walidacja
    if (empty($order['items'])) {
        throw new Exception("Empty order");
    }

    if (empty($order['user']['email'])) {
        throw new Exception("Invalid user");
    }

    // Obliczanie sumy
    $total = 0;
    foreach ($order['items'] as $item) {
        $total += $item['price'] * $item['qty'];
    }

    // Wysyłanie maila — hard-coded SMTP i logika w jednym miejscu
    $smtp = "smtp.server.com"; // na sztywno

    mail($order['user']['email'], "Order processed", "Your total is: $total");

    return $total;
}

email_client.php (z długiem)

<?php

class EmailClient {
    public function send($email, $message) {
        $smtp = "smtp.server.com"; // na sztywno
        echo "Sending via $smtp to $email: $message";
    }
}

index.php (uruchamianie projektu z długiem)

<?php

require 'order_processor.php';
require 'email_client.php';

$order = [
    'user' => [ 'email' => 'client@example.com' ],
    'items' => [
        ['price' => 10, 'qty' => 2],
        ['price' => 5, 'qty' => 5]
    ]
];

$total = processOrder($order);
echo "Total: $total";

Część II: Projekt po refaktoryzacji (spłata długu)

Po spłacie długu:

  • Logika jest oddzielona od wysyłania e-maili.
  • Brak hard-codingu.
  • Kod jest modularny i testowalny.
  • Każda funkcja ma jedną odpowiedzialność.

order_processor.php (po refaktoryzacji)

<?php

require_once 'validator.php';
require_once 'calculator.php';
require_once 'email_client.php';

function processOrder($order, EmailClient $emailClient) {
    validateOrder($order);

    $total = calculateTotal($order);

    $emailClient->send($order['user']['email'], "Your order total is $total");

    return $total;
}

validator.php

<?php

function validateOrder($order) {
    if (empty($order['items'])) {
        throw new Exception("Empty order");
    }

    if (empty($order['user']['email'])) {
        throw new Exception("Invalid user");
    }
}

calculator.php

<?php

function calculateTotal($order) {
    $total = 0;
    foreach ($order['items'] as $item) {
        $total += $item['price'] * $item['qty'];
    }
    return $total;
}

email_client.php (po refaktoryzacji)

<?php

class EmailClient {
    private $smtp;

    public function __construct($smtp) {
        $this->smtp = $smtp;
    }

    public function send($email, $message) {
        echo "Connecting to {$this->smtp}\n";
        echo "Sending to $email: $message";
    }
}

index.php (po refaktoryzacji)

<?php

require_once 'order_processor.php';
require_once 'email_client.php';

$emailClient = new EmailClient('smtp.server.com');

$order = [
    'user' => [ 'email' => 'client@example.com' ],
    'items' => [
        ['price' => 10, 'qty' => 2],
        ['price' => 5, 'qty' => 5]
    ]
];

$total = processOrder($order, $emailClient);
echo "Total: $total";

Wnioski

Projekt po refaktoryzacji jest:

  • modularny,
  • testowalny,
  • pozbawiony hard-codingu,
  • zgodny z zasadą pojedynczej odpowiedzialności (SRP).

To prosty przykład, ale pokazuje ideę długu technologicznego i sposoby jego spłaty.

Overengineering jako źródło długu technologicznego

Choć dług technologiczny zwykle kojarzy się z pisaniem kodu „na skróty”, równie groźnym zjawiskiem jest overengineering, czyli tworzenie rozwiązań zbyt złożonych w stosunku do faktycznych potrzeb biznesowych.

Overengineering polega na projektowaniu skomplikowanych architektur, wielu warstw abstrakcji, nadmiernych wzorców projektowych czy rozbudowanych struktur, które nie przynoszą realnej wartości. Paradoksalnie — próba stworzenia „idealnego”, przesadnie elastycznego systemu może wygenerować więcej długu technologicznego niż podejście pragmatyczne.

Dlaczego overengineering tworzy dług technologiczny?

1. Zwiększona złożoność utrudnia rozwój

Każda dodatkowa warstwa, interfejs czy wzorzec projektowy wymaga zrozumienia przez zespół. Im bardziej skomplikowany system, tym trudniej go modyfikować, co podnosi koszt pracy.

2. Więcej kodu, więcej błędów

Niepotrzebna architektura oznacza większy obszar potencjalnych awarii — tzw. complexity debt.

3. Wyższy koszt wdrażania nowych developerów

Nowi członkowie zespołu muszą poznać nie tylko kod, ale i wszystkie niestandardowe abstrakcje, co spowalnia onboarding.

4. Spowolnienie rozwoju funkcjonalności

Przekombinowana architektura wydłuża czas implementacji nawet prostych zmian.

5. Przedwczesna optymalizacja nie pasuje do rzeczywistych potrzeb

Overengineering często wynika z prób rozwiązania problemów, które jeszcze nie istnieją.

Przykład overengineeringu jako długu

Załóżmy, że aplikacja musi jedynie zapisywać zamówienia do bazy. Zamiast prostego rozwiązania powstaje:

  • wiele warstw abstrakcji,
  • kilkanaście interfejsów,
  • pełne CQRS,
  • rozbudowany EventBus,
  • pełne DDD z agregatami i value objects.

Dla małego projektu efektem jest ogromny koszt dodania każdej zmiany. To właśnie dług technologiczny, mimo że kod wygląda „czysto” i „idealnie”.

Kiedy overengineering staje się długiem technologicznym?

Overengineering to dług, gdy:

  • komplikuje realizację prostych zadań,
  • utrudnia utrzymanie,
  • obniża tempo pracy zespołu,
  • wprowadza kod, który nie wnosi wartości,
  • powoduje rosnące koszty rozwoju.

Overengineering vs underengineering – porównanie

Aspekt Underengineering (za mało jakości) Overengineering (za dużo jakości)
Złożoność Za niska Zbyt wysoka
Przewidywalność błędów Wysoka Wysoka (z innego powodu)
Koszt rozwoju Rośnie, bo brakuje jakości Rośnie, bo jakość jest nieadekwatna
Tempo zmian Spada z powodu chaosu Spada z powodu złożoności
Korzeń problemu Skróty i pośpiech Przesadna przyszłościowa architektura

Jak zarządzać długiem technologicznym?

Aby dług nie wymknął się spod kontroli:

  • Planuj czas na refaktoryzację.
  • Regularnie analizuj kod narzędziami (np. PHPStan, Psalm, SonarQube).
  • Stosuj code review i standardy kodowania.
  • Dodawaj testy automatyczne.
  • Unikaj powtarzalnego kodu, hard-codingu i zbyt złożonych funkcji.
  • Aktualizuj biblioteki i frameworki.

Podsumowanie

Dług technologiczny jest normalną częścią rozwoju oprogramowania — kluczowe jest świadome zarządzanie nim. Powyższe przykłady w PHP pokazują, jak łatwo powstaje oraz jak niewielkimi zmianami można go redukować. Dzięki temu projekt staje się stabilniejszy, szybszy w utrzymaniu i tańszy w dalszym rozwoju.

Overengineering, mimo pozornej dbałości o jakość, jest częstą przyczyną długu technologicznego. Dług powstaje nie tylko wtedy, gdy tworzymy kod „zbyt słaby”, ale również wtedy, gdy jest on „zbyt dobry” w nieodpowiednim miejscu. Kluczowe jest utrzymanie równowagi i tworzenie rozwiązań adekwatnych do skali projektu.

21 listopada 2025 4

Kategorie

programowanie

Dziękujemy!
()

Powiązane wpisy


Informacja o cookies

Moja strona internetowa wykorzystuje wyłącznie niezbędne pliki cookies, które są wymagane do jej prawidłowego działania. Nie używam ciasteczek w celach marketingowych ani analitycznych. Korzystając z mojej strony, wyrażasz zgodę na stosowanie tych plików. Możesz dowiedzieć się więcej w mojej polityce prywatności.