Thinking in terms of intents — the best friend of software developer

Jakub Pruszyński
5 min readMar 13, 2021

Another look on clean code

First things first, let’s start with the answers for some of the basic questions about software.

Why do we create software?

We create it because somewhere there is a problem which can be solved by our application. It doesn’t matter, if it is a need of social interactions or an easy access to bank account. There is always a trouble to solve out.

For whom do we write code?

We write code for people. It means, that we are delivering all those fancy functionalities, which everyone is looking for. On the other hand is for the business to make some money ;) Alright stop with the joke here. That’s not all receivers. We cannot forget about ourselves — software developers. After all we are the ones who reads, creates and modifies hundreds of lines each day.

So what do you mean by thinking in terms of intents, and what the heck do you understand by intents itself?

In a dictionary we can find definition suited for us :

Intents or Intentions — purpose or attitude toward the effect of one’s actions

Sounds reasonably. From here we can define the term from subject.
It is worth to mention that this concept has been introduced for the purposes of this article. However you may find many similarities and references in professionals materials attracted by the topic of business in IT.

Thinking in terms of intents — approach constantly asking ourselves what this or that piece of code solves in our problem area, and representing it by proper naming and structuring in scope of application.

But how it all connects in software development ?

Photo by Markus Spiske on Unsplash

Let’s do a simple example of unintentional code making some calculations.
In meanwhile try to answer below questions.

  • What is the problem area ?
  • What does the code do ?
  • What are the business needs ?
  • Will you remember why you wrote it after a few months ?
fun sumNumbers(a: List<Int>): Int {
var sum = 0
for (x: Int in a){
sum = sum + x
}
return sum
}

fun take19PercentFromNumber(c: Int): Double {
return c * 0.19
}

fun main() {

val a = 4321
val b = 1234
val numbers = listOf(a, b)
val c = sumNumbers(numbers)

val d = take19PercentFromNumber(c)
}

It works and has proper format. So now, the tricky part — the answers.

  • What is the problem area? — Too little information.
  • What does the code do ? — It takes 19% sum of some numbers. That the best conclusion we can make.
  • What are the business needs ? — Too little information.
  • Will you remember why you wrote it after a few months ? — Probably not.

How many points, were matched with yours ? Conclusion is one the interpretation looks foggy, and it’s more like guessing.

Then give it another shot, and this time by thinking in terms of intents representing our problem. Do not forget to answer the same questions once again.

fun getTotalIncome(incomes: List<Int>): Int {
var totalIncome = 0
for (incomeItem: Int in incomes){
totalIncome = totalIncome + incomeItem
}
return totalIncome
}

fun calculateTaxToPay(totalIncome : Int): Double {
return totalIncome * 0.19
}

fun main() {

val incomeFromFirstJob = 4321
val incomeFromSecondJob = 1234
val incomes = listOf(incomeFromFirstJob, incomeFromSecondJob)
val totalIncome = getTotalIncome(incomes)

val taxToPay = calculateTaxToPay(totalIncome)
}

The code suddenly became quite obvious and the answers expose to us in a very direct way.

  • What is the problem area? — Tax payment
  • What does the code do ? — It calculates total income tax of earnings.
  • What are the business needs ? —Calculation of total income tax of a few sources.
  • Will you remember why you wrote it after a few months ? —Maybe not, but a quick read will be enough to understand it, once more.

As we can see, our business needs match with the code responsibility, and that is something we can call here, the magic. We always should look for it in our apps, after all we wrote it for business and for us, developers. Now, each line is intentional and tells us explicitly, what is her purpose. Thanks to that, we lighten our brain of remembering, or reanalyzing code in the future.
This simple illustration has only twenty lines, but impact by only changing our approach is tremendous, we focused on what code represents, not how.

The same rule applies to higher levels of abstraction such as classes, interfaces, or even modules. Of course, I’m going to, present it in a minute.

Let’s do another example, more project-fashion. Usually, each app needs some kind of persistence layer. That layer is used by a domain which should always lay in the center of our software. In the middle of these two territories there is usually a contract in a form of repository or data access object interface. It is exposed by the domain to present her concerns. So, imagine our application manages users with functionalities, like changing user login or changing password, we forget about other actions for brevity. Our first thought about that contract — interface may look like that.

interface UserDao {    //other methods

fun getUser(userId: UserId): User
fun saveUser(user: User): User
}

It’s very likely that you have seen something like this. It works and can satisfied all our requirements. Is it the best way to do it ? Are the business needs present ? The answer is no. It lacks on purpose of usage. That kind of approach may lead to putting all the responsibility on a client’s back or checking for differences property by property in the implementation.
Both scenarios are error-prone and unmaintainable. Let’s try find a better, intentional way, just by focusing on our project business needs.

interface UserDao {    // other methods    fun getUser(userId: UserId): User    fun changePassword(
userId: UserId,
userPassword: UserPassword
): User
fun changeLogin(user:Id, userLogin: UserLogin): User
}

Now, as we can see each problem is solved by separate method, named accordingly to the problem and focused only to unreval one thing. There are explicit intentions behind that interface. Thanks to that we gave ourselves testable, SOLID software with easy and pleasant management.

To sum up and finish for now, in my opinion both cases showed you why we should always think in terms of intents. So, dare yourself to ask over and over the same question — Can I do it better in more explicit, business-focused way ?. Happy coding.

Mind blow for curiosity

Photo by Diego PH on Unsplash

In methodology of Domain Driven Design or DDD for short, there is concept of Ubiquitous Language.

Ubiquitous Language - language structured around the domain model and used by all team members to connect all the activities of the team with the software. — source Wikipedia

Creating such namespace for project is always a great idea, even if you are only developer in a team.

--

--