Ο πελάτης ρωτά: "Αφού η προσθήκη αυτής της λειτουργίας σε μια νέα εφαρμογή θα έπαιρνε μια εβδομάδα, γιατί εδώ χρειάζονται τρεις εβδομάδες;"
Αυτή είναι μια πολύ καλή ερώτηση.
Και συχνά η απάντηση δεν είναι: "επειδή οι προγραμματιστές δουλεύουν πιο αργά".
Το πρόβλημα μπορεί να βρίσκεται πολύ βαθύτερα - στην αρχιτεκτονική του συστήματος, στις εξαρτήσεις του, στον τρόπο αποθήκευσης των δεδομένων, στην έλλειψη δοκιμών, στις ιστορικές αποφάσεις και στις επόμενες αλλαγές που προστέθηκαν με τα χρόνια.
Ακριβώς γι’ αυτό το κόστος ανάπτυξης λογισμικού δεν είναι σταθερό.
Η ίδια λειτουργία μπορεί να κοστίζει εντελώς διαφορετικό ποσό σε δύο διαφορετικά συστήματα.
Ο κώδικας δεν κοστολογείται μόνο με βάση τον αριθμό των λειτουργιών
Με την πρώτη ματιά η εργασία μπορεί να φαίνεται απλή.
"Ας προσθέσουμε τη δυνατότητα εξαγωγής δεδομένων στο Excel."
Ή: "Ας προσθέσουμε έναν νέο ρόλο χρήστη."
Ή: "Ας συνδέσουμε το σύστημα με το CRM μας."
Το πρόβλημα είναι ότι μια λειτουργία δεν υπάρχει ποτέ εντελώς αποκομμένη από το υπόλοιπο σύστημα.
Η νέα λειτουργικότητα μπορεί να απαιτεί αλλαγές σε:
- τη βάση δεδομένων,
- το API,
- το backend,
- το frontend,
- το σύστημα δικαιωμάτων,
- τη σύνδεση,
- την αναφορά,
- τις διασυνδέσεις,
- τις δοκιμές,
- τους μηχανισμούς cache,
- την τεκμηρίωση,
- τη διαδικασία ανάπτυξης.
Όσο πιο διασυνδεδεμένο είναι το σύστημα, τόσο περισσότερα στοιχεία πρέπει να αναλυθούν πριν από την αλλαγή.
Το μεγαλύτερο κόστος μπορεί να προκύψει πριν γραφτεί η πρώτη γραμμή κώδικα
Σε ένα ώριμο σύστημα, ο προγραμματιστής δεν πρέπει απλώς να αρχίσει να γράφει.
Πρώτα πρέπει να απαντηθούν τα εξής:
- Πού θα πρέπει να προστεθεί αυτή η λειτουργία;
- Με ποια υποσυστήματα θα επικοινωνεί;
- Ποια δεδομένα χρησιμοποιεί;
- Οι υπάρχοντες μηχανισμοί δικαιωμάτων την καλύπτουν;
- Θα επηρεάσει η αλλαγή άλλες διαδικασίες;
- Ποιες δοκιμές πρέπει να ενημερωθούν;
- Επιτρέπει καν η τρέχουσα αρχιτεκτονική να γίνει σωστά αυτό;
Όλα αυτά αποτελούν μέρος του κόστους υλοποίησης της λειτουργίας.
Γι’ αυτό σε ένα παλιό σύστημα σημαντικό μέρος της δουλειάς μπορεί να μην είναι ο ίδιος ο προγραμματισμός, αλλά η αναγνώριση των εξαρτήσεων και των περιορισμών της υπάρχουσας λύσης.
Το technical debt λειτουργεί σαν τόκοι
Ένας καλός τρόπος να σκέφτεται κανείς το technical debt είναι ακριβώς το κόστος των επόμενων αλλαγών.
Αν μια λύση κάποτε έγινε γρήγορα, μπορεί να ήταν απολύτως δικαιολογημένη.
Το πρόβλημα εμφανίζεται όταν η προσωρινή λύση γίνεται μόνιμο στοιχείο του συστήματος.
Προκύπτει μια νέα λειτουργία.
Έπειτα άλλη μία.
Εμφανίζεται μια εξαίρεση.
Έπειτα άλλη μία εξαίρεση.
Σε αυτό προστίθεται η διασύνδεση, το παράκαμψη του προβλήματος, η χειροκίνητη διαδικασία και ένας επιπλέον κανόνας.
Μετά από λίγα χρόνια, κανείς δεν θυμάται πλέον γιατί το σύστημα λειτουργεί έτσι ακριβώς.
Αλλά κάθε επόμενη αλλαγή πρέπει να λάβει υπόψη όλες αυτές τις ιστορικές αποφάσεις.
Ο Martin Fowler περιγράφει το technical debt ως την επιπλέον προσπάθεια που απαιτείται κατά τις αλλαγές στο σύστημα εξαιτίας προβλημάτων στην εσωτερική του ποιότητα.
Άρα μπορούμε να πούμε: το technical debt δεν χρειάζεται να σταματήσει αμέσως την ανάπτυξη. Πρώτα κάνει κάθε επόμενη αλλαγή πιο ακριβή.
Σήμα πρώτο: "παρεμπιπτόντως πρέπει να διορθωθούν άλλα πέντε πράγματα"
Αυτό είναι ένα από τα πιο χαρακτηριστικά συμπτώματα.
Ο πελάτης παραγγέλνει μία λειτουργία.
Κατά την ανάλυση αποδεικνύεται ότι για να υλοποιηθεί πρέπει:
- να διορθωθεί η δομή του πίνακα,
- να αλλάξει ο τρόπος εξουσιοδότησης,
- να ενημερωθεί η βιβλιοθήκη,
- να διορθωθεί το παλιό API,
- να ξαναγραφεί ένα τμήμα του frontend.
Ξαφνικά η μικρή λειτουργία παύει να είναι μικρή λειτουργία. Όχι επειδή η απαίτηση είναι περίπλοκη. Επειδή το σύστημα δεν έχει πλέον τα κατάλληλα αρχιτεκτονικά όρια.
Σήμα δεύτερο: μία αλλαγή απαιτεί δοκιμή ολόκληρου του συστήματος
Αν μια μικρή τροποποίηση απαιτεί πλήρη χειροκίνητο έλεγχο παλινδρόμησης, ο οργανισμός πληρώνει για την έλλειψη αυτοματοποίησης.
Καθώς το σύστημα μεγαλώνει, ο αριθμός των πιθανών συνδυασμών αυξάνεται.
Χωρίς το κατάλληλο σύνολο δοκιμών, γίνεται όλο και πιο δύσκολο να είμαστε βέβαιοι ότι η νέα λειτουργία δεν κατέστρεψε την παλιά.
Αυτό με τη σειρά του οδηγεί σε προσεκτικότητα.
Οι αναπτύξεις γίνονται πιο σπάνιες.
Οι αλλαγές γίνονται μεγαλύτερες.
Το ρίσκο αυξάνεται.
Και οι μεγαλύτερες αναπτύξεις είναι δυσκολότερο να διαγνωστούν σε περίπτωση προβλημάτων.
Δημιουργείται ένας φαύλος κύκλος.
Σήμα τρίτο: "αυτό το υποσύστημα καλύτερα να μην το αγγίξουμε"
Αυτή η φράση θα έπρεπε να ανάψει ένα προειδοποιητικό λαμπάκι.
Αν ένα συγκεκριμένο υποσύστημα έχει γίνει περιοχή που η ομάδα αποφεύγει, επειδή η συμπεριφορά του είναι απρόβλεπτη, τότε το σύστημα έχει σοβαρό πρόβλημα συντηρησιμότητας.
Ακόμα χειρότερα, αν τη λειτουργία του τη γνωρίζει μόνο ένα άτομο. Τότε η εταιρεία δεν έχει μόνο technical debt. Έχει επίσης knowledge risk.
Η αποχώρηση ενός εργαζομένου μπορεί να σημαίνει απώλεια της γνώσης που απαιτείται για την ασφαλή εξέλιξη του συστήματος.
Σήμα τέταρτο: κάθε λειτουργία απαιτεί εξαιρέσεις
Ένα καλά σχεδιασμένο σύστημα πρέπει να έχει προβλέψιμους κανόνες.
Αν κάθε επόμενη λειτουργία απαιτεί την προσθήκη ειδικής εξαίρεσης, πρόσθετης συνθήκης ή μεμονωμένης διαδρομής, η αρχιτεκτονική πιθανότατα αρχίζει να περιορίζει την ανάπτυξη.
Αυτό συχνά οδηγεί σε κώδικα που δεν μπορεί πλέον να προβλεφθεί εύκολα.
Και η έλλειψη προβλεψιμότητας σημαίνει μεγαλύτερο κόστος ανάλυσης, δοκιμών και συντήρησης.
Πρέπει να ξαναγραφτούν όλα;
Όχι.
Και εδώ φτάνουμε σε μια πολύ σημαντική διάκριση.
Το technical debt δεν σημαίνει αυτόματα ότι απαιτείται rewrite.
Οι πιθανές λύσεις περιλαμβάνουν:
Refactoring
Δηλαδή τη βελτίωση της δομής του υπάρχοντος κώδικα χωρίς να αλλάζει η επιχειρησιακή του συμπεριφορά.
Είναι καλή κατεύθυνση όταν το σύστημα εξακολουθεί να έχει λογική αρχιτεκτονική, αλλά συγκεκριμένα τμήματα είναι δύσκολα στη συντήρηση.
Εκσυγχρονισμό επιλεγμένων στοιχείων
Δεν χρειάζεται να αντικατασταθεί ολόκληρη η εφαρμογή.
Μπορεί κανείς να ξεκινήσει από το πιο προβληματικό υποσύστημα, τη διασύνδεση ή το επίπεδο.
Σταδιακή μετάβαση
Τα νέα στοιχεία μπορούν να λειτουργούν δίπλα στο παλιό σύστημα, και οι επόμενες περιοχές να μεταφέρονται σταδιακά.
Αυτή η προσέγγιση επιτρέπει τον περιορισμό του κινδύνου μιας εφάπαξ μετάβασης. Στη βιβλιογραφία σχετικά με το legacy modernization χρησιμοποιείται συχνά ακριβώς η σταδιακή αποσύνθεση της λειτουργικότητας και η αντικατάσταση των επόμενων τμημάτων του συστήματος.
Rewrite
Η δημιουργία ενός νέου συστήματος έχει νόημα όταν η τρέχουσα αρχιτεκτονική είναι τόσο περιοριστική που η περαιτέρω εκσυγχρονιστική παρέμβαση δεν δίνει δικαιολογημένη απόδοση.
Αλλά το rewrite θα πρέπει να είναι απόφαση που προκύπτει από ανάλυση, όχι αντίδραση στη frustration της ομάδας.
Πότε δεν αξίζει ακόμη να επενδύσουμε στον εκσυγχρονισμό?
Technical debt in itself is not a reason to stop development. Every system has some level of technical debt. Sometimes paying it down does not make economic sense.
If the application:
- works reliably,
- is secure,
- has a small number of changes,
- supports a process that will not grow significantly,
- does not generate operational problems,
it may be reasonable to leave it as it is.
The point is not for every system to be technologically perfect.
The point is for the level of debt to be a conscious decision.
When does the cost of debt become a business problem?
When it starts affecting the company’s results.
For example:
A new feature was supposed to hit the market in a month, but it needs three.
Integration with a new partner is dragging on because the old system’s API does not easily allow new data to be handled.
A key person on the team has to take part in the work every time, because only they know the old module.
Every major deployment requires hours of regression testing.
A competitor introduces new features faster because its platform allows quicker experimentation.
At this point, technical debt stops being an IT department problem.
It becomes a business problem.
How can you measure whether the situation is getting worse?
There is no need to create a complicated KPI system.
It is worth monitoring a few simple indicators:
Lead time - how much time passes from starting work on a change to deploying it.
Deployment frequency - how often the team can safely deliver changes.
Change failure rate - how often deployments cause problems.
Recovery time - how quickly you can return to stable operation after a failure.
Feature lead time - whether similar tasks require more and more effort.
It is also worth analyzing the number of manual operations, test coverage, dependency freshness, and the time needed to onboard a new developer to the project.
Such data makes it possible to see whether the problem is truly technical, or whether it stems from the process, requirements, or the way work is organized.
The worst solution is "just one quick fix"
If the team knows the architecture needs changes, but keeps putting the topic off each time, the system can enter a spiral.
"Let’s do a workaround for now."
"We’ll do the refactoring later."
"For now, this is enough."
"At the next release."
The problem is that the next release brings more requirements.
And every subsequent workaround increases the cost of the next change.
That is why the decision to pay down technical debt should be part of the product development strategy, not an accidental reaction to a crisis.
A good application is not one that never gets old
Every system will change.
Technologies will change.
Customers will have new needs.
New integrations will appear.
The company’s way of working will change.
That is why the goal should not be to create an application that never needs to be modernized. The goal should be to create an architecture in which modernization is possible without stopping the business. That is a huge difference.
Because the best system is not the one that looks the most modern on the day it launches. It is the one that still allows the company to respond quickly to changes years later.
And if every new feature costs more and more, that does not always mean the feature is difficult.
Perhaps the system itself has become difficult.



