Making Java code more readable with Type Abstractions

Let’s consider a simple example project, appointments, where each appointment has a date, doctor, patient, and comments. These records are read from input, displayed in list or tabular form and saved. The code involves multiple layers — reading and writing I/O, formatting data, and writing formatted views.

Here’s the actual Java representation of an appointment:

public record Appointment(LocalDate date, String doctor, String patient, String comments) {}

Early in the design, many of the functions that manipulate these appointments had verbose, deeply nested type signatures like BiFunction<List<String>, Supplier<Stream<List<String>>>, String>. While correct, such signatures obscure intent. That’s where type abstractions come in — small, domain-specific type aliases that make the structure of the code clearer and more readable.

The Problem: Overly Complex Function Signatures

Consider this example, which takes a list of headers and a content supplier, producing a string representation of a list view (the table view is omitted for brevity) :

public static BiFunction<List<String>, Supplier<Stream<List<String>>>, String>
listFormat =
    (headers, content) ->
        header(headers).append(data(content)).toString();

This works, but the type signature is long and opaque. It doesn’t immediately tell you what the function is about — only that it’s a BiFunction of lists and streams that returns a string.

The Solution: Creating Semantic Type Aliases

Java doesn’t have native type aliases, but we can emulate them through interfaces that extend existing types. For instance, we can define a View type that captures the concept of a function that takes headers and content, and returns a formatted string:

public interface Types {
    interface View extends BiFunction<
            Collection<String>,
            Supplier<Stream<Collection<String>>>, String> {}
}

With this, our previous function becomes much cleaner:

public static View listFormat = (headers, content) ->
  header(headers).append(data(content)).toString();

The functionality hasn’t changed — but the intent is now explicit. We’ve moved from a generic, mechanical type to a domain-level concept: a View.

Extending the Abstractions

Next, the display function — responsible for rendering appointments — takes a View (the formatter) and returns a ViewWriter<IO> (the executor that writes the formatted output to an IO stream). Originally, its signature was difficult to read:

public static Function<
    BiFunction<List<String>, Supplier<Stream<List<String>>>, String>,
    BiFunction<List<String>, Supplier<Stream<List<String>>>, Consumer<IO>>>

display = ...

Using type abstractions, this becomes far more expressive:

public static Function<View, ViewWriter<IO>> display =
view ->
  (headers, content) -> io ->
    io.print(content.get().count() == 0 ? "No appointments found\n"
      : view.apply(headers, content));

Here’s how the types break down:

  • View — the formatter: takes headers and content, produces a string representation of the table.
  • ViewWriter<W> — the executor: takes headers and content, and produces a side effect by writing the formatted string to a consumer of type W (like IO).
interface ViewWriter<W> extends BiFunction<
        Collection<String>,
        Supplier<Stream<Collection<String>>>,
        Consumer<W>> {}

This separation keeps formatting logic distinct from side-effect logic: View handles what the output looks like, and ViewWriter handles where it goes.

Abstracting Read and Write Operations

Finally, the function responsible for reading a new appointment from input and writing it is defined as:

public static ReadWriter<Appointment, TypedIO> addNew =

    writer ->
      reader ->
        writer.accept(new Appointment(
            reader.readDate("Enter date: ", "invalid date"),
            reader.readString("Enter doctor: ", "").orElse(""),
            reader.readString("Enter patient: ", "").orElse(""),
            reader.readString("Enter comments (if any): ", "").orElse("")));

Its type alias is equally simple and expressive:

interface ReadWriter<R, W> extends Function<Consumer<R>, Consumer<W>> {}

Rather than dealing with nested Function<Consumer<X>, Consumer<Y>> constructs, we now have a ReadWriter — a function that connects two side effects: reading and writing.

The Result

These abstractions don’t change the runtime behavior of the code. They change its shape.
Now, a developer reading Function<View, ViewWriter<IO>> can immediately tell that the function takes a view and returns a view writer — no decoding of generic types required.

The result is clearer, safer, and more expressive code. Type abstractions let us represent not just data, but also intent, in the type system. They make it easier to reason about functions, compose behaviours, and test side effects in isolation — all without adding boilerplate or runtime overhead.

(Adapted from Chapter 3, “Type Abstractions,” in Dr. Software, available at bitgloss.ro/dr-software.pdf

Full example code found here)

Artificial Intelligence: A misleading name for powerful tools

This article is a follow-up of my 2019 article on the same topic, but updated in the light of today’s AI hype.

Like I mentioned then, when I first experimented with neural networks years ago, I was struck by how little “intelligence” there actually was in them. They did exactly what I coded them to do—no more, no less. Today’s systems may look more impressive, but the principle hasn’t changed: these are tools, not minds.

The Problem With the Word “Intelligence”

Humans don’t even have a precise definition of their own intelligence or consciousness. If we can’t define those terms for ourselves, it makes no sense to claim that we’ve created them in machines.

What we can say is that current AI systems don’t exhibit the qualities most people intuitively link with intelligence: self-awareness, understanding, intent, or meaning. They generate outputs by compressing patterns in data, not by reasoning or knowing.

To call that “intelligence” is to confuse statistical mimicry with cognition.

What AI Actually Does

Modern AI systems, especially large language models and generative tools, are best understood as:

Pattern machines. They excel at finding correlations in vast amounts of data.

Automation engines. They can handle repetitive, data-heavy tasks quickly and consistently.

Amplifiers. They extend human capability, but only within boundaries set by training data and design.

This is powerful, but it is not thought.

The 2025 Reality Check

Scale isn’t sentience. More data and bigger models don’t bring us closer to human-like understanding.

Usefulness ≠ understanding. A tool can be highly practical without being intelligent.

The real risks are human. Bias, misuse, privacy abuse—these are problems in how people deploy the systems, not evidence of AI “deciding” anything.

Why the Distinction Matters

If we keep pretending AI is a kind of mind, we risk treating its outputs as if they were grounded in meaning or truth. They aren’t. They’re grounded in probabilities.

AI is not intelligent because we don’t even know what that word would mean in this context. What we do know is that these systems are fundamentally different from human thought: they calculate, predict, and generate—but they do not understand.

Conclusion

The danger isn’t that AI will “wake up.” The danger is that humans will forget what it actually is: computation dressed up in human-like outputs. Powerful, yes. Useful, yes. But never a mind.

Facturarea modernă și simplă prin worklog.ro

Pentru mulți freelanceri și mici antreprenori, finalul de lună vine cu aceeași rutină: adunarea orelor lucrate, verificarea contractelor și pregătirea facturilor pentru clienți. Un proces care, de multe ori, consumă timp și energie, mai ales atunci când se face manual sau cu instrumente disparate.

Aici intervine worklog.ro, platforma care integrează nu doar raportarea timpului lucrat, ci și facturarea. În loc să treci prin mai multe aplicații și fișiere, totul este disponibil într-un singur loc, simplu și organizat.


De la ore lucrate la factură emisă

Unul dintre cele mai mari avantaje este legătura directă între jurnalul de lucru și modulul de facturare. Orele înregistrate pentru un proiect se pot importa direct în factură, eliminând complet calculele manuale și riscul de erori.

Tot ce trebuie să faci este să alegi proiectul și să adaugi liniile corespunzătoare. Totalurile se calculează automat, iar factura finală poate fi descărcată imediat în format PDF.


Integrare cu e-factura

În contextul reglementărilor fiscale actuale, e important ca facturile să fie conforme cu cerințele ANAF. worklog.ro oferă opțiunea de a genera facturi pregătite pentru sistemul RO e-Factura și de a le încărca direct acolo, fără pași suplimentari.

Astfel, în câteva minute, documentul nu doar că este emis, ci și înregistrat în sistemul oficial. Este o soluție practică atât pentru firmele mici, cât și pentru colaborările cu parteneri mai mari care cer respectarea acestor standarde.


Flexibilitate și control

Facturarea cu worklog.ro nu se rezumă doar la emiterea unui document. Ai la dispoziție:

  • posibilitatea de a copia rapid o factură anterioară,
  • opțiuni pentru ștergere (în aceeași zi, dacă nu a fost încărcată în e-factura),
  • funcționalitatea de storno, atunci când e nevoie să corectezi o situație,
  • vizualizarea facturilor primite, inclusiv cele venite din sisteme externe.

Practic, ai o imagine de ansamblu completă, într-un singur tablou de bord.


Mai mult timp pentru business, mai puțin pentru birocrație

Atunci când îți emiți facturile cu worklog.ro, câștigi două lucruri esențiale: timp și siguranță. Nu mai pierzi ore întregi cu verificări repetitive, nu mai riști să omiți detalii și știi că documentele tale sunt conforme cu legislația.

Pentru detalii tehnice și exemple concrete de utilizare, poți consulta documentația oficială de facturare.


Încearcă și tu modulul de facturare din worklog.ro. Vei descoperi cât de simplu poate fi să îți organizezi activitatea și să îți gestionezi clienții fără stresul facturilor de la final de lună.


De la haos la claritate: povestea unui final de lună cu worklog.ro

Este joi seara, aproape de finalul lunii. Andrei, freelancer în IT, primește un email de la client:

„Ne poți trimite un raport cu orele lucrate în ultimele patru săptămâni?”

Pare o cerere simplă, dar Andrei știe ce urmează: câteva ore pierdute căutând prin Excel-uri, notițe pe telefon și mesaje vechi din Slack. Nu e prima dată când se întreabă dacă nu a uitat să factureze câteva zile.

Așa arată realitatea pentru mulți freelanceri și echipe mici: multă muncă, dar evidențe împrăștiate. Iar finalul de lună devine un stres în loc să fie o formalitate.


Cum arată alternativa?

Acum, imaginează-ți aceeași situație, dar cu un mic detaliu diferit: Andrei folosește worklog.ro.

În loc să caute prin fișiere, deschide aplicația. Acolo găsește toate orele deja înregistrate, împărțite pe proiecte, cu descrierile aferente. Selectează perioada, apasă pe „Generează raport” și, în câteva secunde, are un document clar, gata de trimis clientului.

Stresul dispare. Raportul arată profesionist, iar Andrei câștigă câteva ore pe care altfel le-ar fi pierdut făcând calcule manuale.


Ce face worklog.ro diferit?

Secretul e simplitatea. În fiecare zi, când termină o sarcină, Andrei adaugă câteva detalii: data, numărul de ore, descrierea și proiectul. Atât. Intrarea apare imediat în jurnal.

Dacă are de lucrat pe același proiect mai multe zile la rând, folosește funcția Adăugare multiplă și economisește timp. Dacă are nevoie să reia o activitate mai veche, găsește rapid ultimele 100 de înregistrări în secțiunea Past items.

Iar dacă lucrează cu echipe care folosesc Jira, orele pot fi importate direct, fără muncă suplimentară. Pentru cei care preferă Excel, există și importul CSV. Practic, Worklog se adaptează felului în care lucrezi deja.


Beneficiile reale

Pentru Andrei, și pentru oricine lucrează pe bază de proiecte, asta înseamnă:

  • claritate – știe exact cât și pe ce a lucrat;
  • profesionalism – clienții primesc rapoarte clare, fără erori;
  • timp câștigat – orele pierdute cu administrarea se reduc la câteva minute;
  • siguranță financiară – nicio oră lucrată nu rămâne nefacturată.

Povestea de final de lună, rescrisă

În loc să rămână până noaptea târziu să adune orele, Andrei își încheie ziua liniștit. Trimite raportul în câteva minute și știe că este complet și corect. Clientul îl primește la timp și apreciază organizarea.

Finalul de lună devine previzibil, nu stresant.


Concluzie

Dacă și tu te regăsești în povestea lui Andrei, poate că e momentul să îți simplifici viața.

Încearcă worklog.ro și vezi cum e să ai mereu control asupra timpului tău, fără să te mai pierzi în hârtii și Excel-uri.

De la înființarea firmei la facturarea clienților – pași practici


1. Deschiderea unei firme în România

Dacă vrei să începi să lucrezi legal cu clienți, trebuie să alegi o formă juridică. Cele mai comune sunt:

  • PFA (Persoană Fizică Autorizată) – simplu de deschis, costuri reduse, dar răspunderea este personală.
  • SRL (Societate cu Răspundere Limitată) – implică mai multă birocrație, însă oferă protecție patrimonială și credibilitate în fața clienților.

Pașii principali pentru un SRL:

  • Alegerea și rezervarea denumirii firmei la ONRC.
  • Stabilirea sediului social (poate fi și la domiciliu).
  • Redactarea actului constitutiv.
  • Depunerea capitalului social minim (200 lei).
  • Înregistrarea la Registrul Comerțului și obținerea certificatului de înregistrare (CUI).
  • Înregistrarea opțională în scopuri de TVA, dacă e necesar.
  • Deschiderea unui cont bancar pentru firmă – obligatoriu pentru SRL, recomandat și pentru PFA. Contul va fi folosit pentru tranzacții și încasarea facturilor.

Mai multe detalii oficiale găsești pe site-ul ONRC – Înmatriculări societăți.


2. Ținerea evidenței orelor lucrate (exemplu simplu în Excel)

Un mod clasic de a urmări timpul lucrat este să folosești un tabel în Excel.

Exemplu de structură:

DataClientProiectActivitateOre lucrateTarif/orăTotal
01.08.2025Client AWebsiteDezvoltare frontend5150750
02.08.2025Client BConsultanțăAnaliză procese interne3200600

Poți găsi modele gratuite de foi de pontaj pe site-ul Microsoft Office Templates.


3. Facturarea manuală în sistemul e-Factura

Din 2024, firmele din România trebuie să trimită facturile către clienți prin RO e-Factura. Sistemul funcționează pe baza unui fișier XML conform standardului european UBL 2.1.

Procesul manual:

  1. Creezi factura în Excel/Word.
  2. Construiești manual un fișier XML conform specificațiilor ANAF.
  3. Te loghezi în Spațiul Privat Virtual (SPV).
  4. Încarci fișierul XML.
  5. Aștepți validarea. În caz de erori, refaci factura și retrimiți.

Este un proces posibil, dar consumator de timp și predispus la greșeli tehnice.


4. Cum simplifică worklog.ro procesul

worklog.ro este o platformă gândită special pentru freelanceri și mici afaceri din România, care își facturează timpul și serviciile.

Beneficii:

  • Logarea simplă a orelor – în loc de Excel, introduci activitățile direct pe clienți și proiecte.
  • Rapoarte lunare instant – afli exact câte ore ai lucrat și cât trebuie facturat.
  • Facturi conforme e-Factura – platforma generează automat fișierul XML conform standardelor ANAF, gata de încărcat în SPV.
  • Reducere erori – nu mai scrii manual coduri XML.
  • Economisești timp – un proces care altfel dura ore devine o chestiune de câteva minute.

Concluzie

Deschiderea unei firme și facturarea clienților implică pași clari și proceduri oficiale. Deși Excel și crearea manuală a XML-urilor sunt soluții de început, ele devin rapid complicate și ineficiente.

Prin worklog.ro, antreprenorii și freelancerii își pot ușura munca: loghează orele direct în platformă, generează facturi conforme e-Factura și scapă de stresul birocrației.


Resurse utile


Automation

A term thrown randomly in lots of software development shops, to describe different things. Why? Automation is a simple term, that used to mean a process that can run without (or with minimal) human intervention. Now it means lots of things and, in most cases, it’s a lie. Let me explain what I mean, through a software development skit:

Alice: Bob, where do we stand with the QA automation for this release?

Bob: We’ll probably have to modify 40% of the regression suite, ’cause lots of tests are failing.

Alice: Do you think we’ll make it in time?

Bob: We could bring in 5 guys from John’s team to help.

Alice: Sounds like a good idea. Let’s do that!

No one ever…

I’m sure you’ve never seen this in practice, ever… Right? “Good idea” what!??? Of course, Alice is acting purely based on her project management targets, but she’s completely missing out on what automation should do for them. That is: reduce the intervention of humans in the process and get faster feedback from that regression suite (as opposed to people playing keyboard monkey roles).

What happened back there? Well, 40% of 10 tests is nothing, so bringing in 5 guys would be one person per test that could help, plus an extra one. The problem could be solved in a matter of minutes…? maybe hours…? Ok, good call Alice! But the elephant in the room, in this scenario, is that the regression suite is actually a joke, not real automation. I mean 10 tests? (yes, I just decided that there were 10 tests, don’t scroll up)

The problem in the large, is that the regression suite has hundreds, if not thousands of tests. This means feedback comes late (hours, sometimes days) after running the suite. Feedback is not clear most of the time, false positives, flakiness and all that good stuff.

Alright. QA automation. But there are other kinds, right? Oh, sure there are. Let’s take the famous “dev-ops pipelines” for example. “What does that even mean? Dev-ops pipeline, pfff…” you say? Stop pretending and just accept you already know what I’m talking about: pull source from GIT onto some jenkins machine, run some stuff from some other jenkins machine and then some stuff on this jenkins machine and then “promote” the buid to some other machine and chop a virgin’s head and make sure all is being pushed to Kibana and kill yourself.

Isn’t that THE automation? Well yes, it is, it’s just that… more often than not it goes wrong at different steps in the process. When was the last time you’ve seen a pipeline like this run well for weeks or months at a time, without human intervention. I can already hear the audience… “months? we’re stepping in a couple of times a day”.

We can see a pattern here: “automation” that hurts rather than helping. Ok, by “hurts” I mean that it does so for the business goals: late deliveries, buggy software products, etc. It totally does NOT hurt the persons which created the “automation” to begin with. To them, it’s an industry standard and if you’re not doing it, you’re so not worth living and must crawl with shame under your desk (not standing desk, of course, ’cause you’re a loser and you’re not trendy). They will defend it to their deaths (that is, until they move to a more trendy company, with bean bag chairs and super-expensive espresso machines).

There are many other kinds of “automation” which generate similar results to the above. Bottom line is: if it constantly hurts the business goals, you’re doing it wrong. “Yes, but my special case…”. NO! Just throw it away (YES, away it should go) and rethink the whole thing. I mean really rethink it in such a way that you won’t have to touch it afterwards, not just use a different technology to do the same thing all over again. It will probably take a bit of time (weeks, months…), but the business will certainly thank you for it.

P.S. What are you doing when you give business a software product? You automate its processes. Why? So they don’t have to do it manually. That business may just as well be you.

Stop mocking your system!

Yes, mocking has a mostly negative meaning! Only this – to imitate (someone or something) closely – doesn’t feel negative.

Mocking is usually used in testing code and it looks something like this:
We have module A which uses modules B and C. When we write tests for module A, we don’t initialise it with the real B and C, but rather “mock” B and C and hand those mocks to A.

All of us have used mocks in our testing code, to substitute the real things. I know I’m definitely guilty of this. Why though? Why do we do it? Well, It’s usually because initialising those collaborators is not trivial (i.e. can’t be done with a one-liner).
But why wouldn’t the collaborators be easy to spin-up? Well, because they have their own issues with their own collaborators and so, you see, the design issues start to emerge. This is why you read things like “TDD makes your design” better (I’m not going to start this now… maybe in a future article).


But let’s backtrack a little bit and be honest with ourselves. What we’re actually doing is faking (please don’t start with the stub/fake flamewar) things that we’ve created ourselves. We lie to ourselves saying that we’re only testing one thing at a time, so having two or more real things breaks best practices. Which best practices? Stack overflow’s most voted answers? Check this out: you set your test victim with mocks and boom, now you have two of the same collaborators living in your codebase. And guess what… you have to keep the mocks in sync with the real things, in case you thought otherwise. Don’t worry, you’ll forget to do that once in a while (I’m being gentle here…) and you know what the end result of that is? Yes, yes, the all too familiar “green tests broken product” syndrome. You know… a dev and a QA walk in to a bar. QA says: “it doesn’t work”. Dev says: “it must be working, all the tests pass”. All the dev tests that is…
And this is exactly where the heart of the problem lies! Creating mocks is just creating an alternative universe for your system, that will eventually lose all connections with reality.
What you almost always want, when mocking, is really just different input data for your module, data normally provided by a long stream of collaborators. Just build your program in such a way that you can send it this data, regardless of the runtime (unit test framework, test env or production env). Yes, it is possible and highly desirable and don’t be in denial right now.

But dude, I don’t want to use a real database (or AWS endpoint or rocket launcher) in my tests. Debatable, but fair enough. Simulating 3rd party systems is acceptable when not doing so would lead to bad consequences in a production environment. Key concepts here are “3rd party” and “production”. If using a 3rd party production system won’t hurt anyone, just do it. Being as close to reality as possible is the best thing to do. Anything else is just a web of lies that we have to maintain.

Mocking your own system is just mocking your own reality. Stop lying to yourself!

Cargo cult programming

…or how to create huge useless programs, that is. In all fairness to the folks doing this, it’s the best they can do, given the knowledge they hold. And this is where I want to make my argument, but first…

Cargo cult programming, says Wikipedia, “is a style of computer programming characterised by the ritual inclusion of code or program structures that serve no real purpose”.
Let’s expand on that a little bit. How can there be code that serves no purpose? What is the purpose? Good questions!

The purpose of the code is to respond to every need of the product. Pretty simple, right? Right! It’s the every need of the product thingy that’s sort of vague and, frankly, it’s not easy to determine EVERY need of the product. There are lots of variables in this domain, some of which are: user preferences, product usage patterns, user base growth, product runtime infrastructure changes, product developers change rate etc. And I mentioned nothing about actual code yet! That’s right, because before having actual code, we need to determine a model for all those variables mentioned before. “Architecture” I hear you say? Whatever… I don’t care how you call it as long as there is some good thinking done first, to address that model.

Please, oh please don’t get hung up on words like: architecture, model etc. This is exactly where cargo cult programming (CCP henceforth – it would have been funny to have another C in there, before the P) stems from.

I’ll give you a method to identify whether or not CCP is employed in your project/product:
Talk to a senior programmer who’s working on the product. Ask them to explain in detail, a small part of the product. Yes! In detail! Tell them to show you the classes (because OOP is almost certainly what you’ll find) and explain how and why they are organised like they are. Take your time, be patient and do your best to follow the explanation. And now the Aha! moment: if the programmer is not eloquent and you don’t understand the explanation or it doesn’t make sense, chances are you’ve got yourself a little CCP going on.
Everything is explainable in layman’s terms… if you understand what you explain, that is. Oh and beware of the “best practice” expression. It usually means: “others on the web are doing it like this and it means this is the way to do it”. So usually “best practice” = CCP (of course it shouldn’t, but that’s what it usually hides behind)

Another way to spot CCP would be to ask programmers what literature they got the ideas from, at which point, more often than not, they would quote websites, articles and blogs (that’s right! You better not be learning programming from this blog!). What you need to hear is books and good authors (e.g. Kent Beck, Robert C. Martin, Martin Fowler, Michael Feathers and many more).

So, CCP is a BAD thing. Repeat after me: CCP is a BAAAD thing. It makes programs less maintainable, more buggy, more expensive. That’s it!

All right. Enough chit-chat! I’ll let you get back to your Wikipedia binge-clicking. Cheers!

Trade-off

Another term that is being overused and misused across our industry… Ok, I want to clear its name a little bit and explain how we actually do trade-offs every day.

Consider this scenario: You need to take over a big legacy system. Really poorly written, no tests, coupling everywhere. You are required to add a couple of minor features and fix a few old bugs. You, you’re a professional programmer, with a proven track record. “They” are product owners which would like those features and bug fixes in production ASAP. Users are demanding justice!

You are faced with a dilemma: How can I, THE programmer, deliver this in an impossible time frame? It’s crazy! It’ll never work… I need to isolate the parts where I’ll have to add those features, I need to write tests to make sure I’m not breaking stuff, I need to refactor the code, to understand it better now and later… all these things, the humanity!!! And what the hell is a m_hWnd pointer?!

In the face of this apocalypse, you might crumble and cry, or you might hit “them” hard until they leave you alone and slowly move you to a “green field” project nobody uses, or… you may consider the following aspects to this problem:

1. The application is in production for years and it produces $$$.

2. Users are pushing.

3. The code is shit.

4. The new features and bug fixes are not really that big.

Now, should you hold your own and tell everyone about point 3, crying how this should inevitably be rewritten and that will take months, years, omg, the humanity!!?? (really author… again with this?) Or instead, should you realise that there is no way in hell you can rewrite this in a timely fashion and just slap some more shit on top, to get it over with and crash like a diva ninja-developer on a chaise longue asking for refreshments?

Well… (get ready for it…) it depends! This is it, thank you folks, good bye!

Wouldn’t you really hate my guts if I ended it there (like some professionals often do)? Hahaha. No my friend. I’m here to help. Here’s what’s gonna happen: you’re going to realise that indeed, you don’t have the time to make it all nice and shiny. But! You won’t slap shit on top of it. You’ll make it so that the new stuff you write is nice and tidy, with tests and all that, but linking it to the legacy system will probably be a little shitty, yes. There’s your first trade-off. The bugs? Well, the bugs get sprayed, but the code defects, you’ll track down with the debugger (ooh, watch out TDD purists), understand what’s going on and apply a small local fix. Second trade-off.

Now, will the code be better after this? Mostly no. But the new parts you wrote will be good and overall, the system will satisfy the angry user mob.

What about your previous articles about clean code and refactoring and all that good stuff (a bit of narcissism, I agree)?

Well, they are part of what I described above too (remember? the “new stuff” you added to the legacy crap). Funny thing is, we expect other people to do these kind of trade-offs every day. Imagine the scenario above, but with a little alteration: you take your car to the mechanic for a couple of small tune-ups and some scratch fixes. Of course you want your car back ASAP and with those things done. What do you think the mechanic does in his little magic castle? Why trade-offs, of course. You get your car back, with the stuff you asked for, you use it, there you go…

So back to the term. Trade-off. It’s actually a placeholder for judgement. The more you think about trade-offs, the more judgement you apply to the problem at hand. So there it is, I guess that’s what I was trying to say.

Quality in software development

What does this truly mean? Somebody actually told me, during a business talk, that quality was just a buzzword. It felt wrong, but in a way it felt real too. I now understand where he was coming from and I can explain it. The only way he experienced quality in software development, was through the word itself: quality. The context around it was usually messy (code, defects, unhappy clients etc.). No wonder he started hating the word and sarcastically referring to it as a buzzword.

So back to the question: what does quality in software development really mean? There is more than one point of view that we should explore, so here we go:

The big picture

Ultimately, everyone wants to get what they ask for, without any hassle and within their budget. The holy grail is getting the best thing, for free, right away. Don’t try to fight this. It’s true. Think of the best version of anything you want and tell me you wouldn’t like it materialising in front of you, right now. So from this point of view, quantification of quality is done on the scale of “this is not what I wanted and it’s too expensive and it takes too long to get” to “this is exactly what I wanted and it’s free and I got it“.

Of course, there are some variations, one of which is heavily encountered in the industry: “this is not quite what I wanted, but it’s cheap, yet it took too long to get“. These are the murky waters many clients jump into when having to do trade-offs. In my experience, these are usually budget trade-offs, that end in an unfortunate state, where over-budget gets spent in the same bucket, but for patch work.

This is the scenario I like best:

Client: This is my problem. Can you solve it?

Provider: Yes.

Client: What will it cost me, in time and money?

Provider: I estimate around 6 months and 500.000$

Client: Great! Let’s do it!

(every couple of weeks)

Provider: Is this what you had in mind?

Client: Almost. Here, I’d like a little more blue.

Provider: Done!

(around 6 months later)

Client: It looks like we’re done. Great job! Thanks!

Provider: Anytime.

There are lots of details behind this scenario, but this is the big picture. When I read it, it gives me a peaceful feeling of “yes, this is how business should be“.

Processes

These are blueprints of behaviour that help when dealing with certain problem patterns. They also help the entities that participate, have some (illusion of) predictability of the future. It’s a sort of guarantee that the future will happen in a certain way. This helps people feel in control and reduces the level of stress. BUT! Since we derive a level of comfort from having the illusion of a certain future, we tend to equate the process with the future itself. This is a mistake that leads to process rigidity and reluctance to step outside its bounds.

I like to look at processes as checkpoints and guidelines along the road. The main focus should always be the product. Engaging my expert skills right now, while actively building it, IS the best guarantee I can have of the future I want. The future is built from a constant stream of present 🙂

I always look at the reason for the existence of the process, so it gives me a better understanding of why it was set up in the first place. This way I can work along the lines of that reason, rather than blindly following a process I cannot understand (and sometimes come to hate).

One thing I noticed is that the fuzzier the goal, the more processes there are. The more processes there are, the more people tend to make a goal out of them. And so the wheel spins…

Craft

This is, surprisingly, not the most looked at factor in the software development industry. In my experience, if a client sees processes and pays an amount that suits her, THE CODE (which is ultimately the product) doesn’t matter as much, as long as it… exists.

WHAT??!! This is outrageous! But wait… is this something the client should even be aware of? Why would it be so? If I buy a pair of shoes, I’m not interested in the way they are built, what I’m really interested in is how they look, how they feel and how long they last.

The craft IS really important, inside the guild. Because we’re talking about code, things should be simple, really: it should work as required (without maintenance, preferably) and it should be easy to modify (if needed).

These are two seemingly straightforward characteristics, but to achieve them, takes not only time and experience, but continuous learning too. A craftsman should be able to easily understand the request (even help with it) and pick the most appropriate tools for the job. A craftsman should be able to read code to business people so that they hear a narrative. A craftsman should be able to write such code.

I want my client to understand where their product stands at any time and what it can do. I do NOT want my client to be forced to listen to what kind of refactoring and how many unit tests I wrote yesterday. Or even worse, she shouldn’t be forced to decide if I need to write them tomorrow.

Also, I don’t want to hear that “we are thinkers, don’t force us to estimate“. Well, if you’re thinkers, then think about how long it takes you to do something you should already know how to do. Nobody said estimates are deadlines (did you?!!), but clients need to have a rough idea about resources they need to allocate. Your GPS doesn’t say “you know what… we’ll get there when we get there! The important thing is the magic I constantly use to display these awesome maps.

Trust

I don’t want to be cheesy and say that trust is earned, because of course it is. The difficult thing is to trust someone I don’t know. Looking at references and traces of past work helps, yes, but I also place a huge weight on the quality of the first discussion. It always served me well in assessing my future collaboration with anyone. Everyone is an intuitive psychologist by nature (not mentioning special health conditions).

The thing is, we almost always know (trained people are an exception, hence the “almost”) when someone is hiding important details in a conversation and we can also “feel” when what they say is what they think.

Obviously, the higher the level of trust, the higher the expected product quality. I’ll stop here and save the psychology magic for another time 🙂

Conclusion

These are my most valuable points of view, when looking at quality in software development. I also look for all of the above in business partners and also, I’m happy to say that for us, at bitGloss, quality is not just a buzzword.