DEV Community

Jasterix
Jasterix

Posted on

How did you break the habit of writing procedural code?

Procedural programming is the first programming paradigm I learned and felt most comfortable with. Even as I learned functional programming, my code has retained a very top-to-bottom, declarative structure, which is limiting in multiple ways.

I'm moving on to object oriented programming role, but still resisting the urge to default to procedural. Even with solving code challenges, I've noticed tendency.

What are some ways to break out of this habit?

Top comments (9)

Collapse
 
jesterxl profile image
Jesse Warden

It's easier if you're in a language that encourages functional thinking while being around developers who think that way too. I'm around Go and Python devs from a variety of backgrounds. The Go developers you'd think would be Functional minded, but they're basically "not OOP". The Python devs are either heavily procedural with an occasional OOP developer. Again, some of the younger devs or those not from a Java background just follow the procedural style because "it's easy, reads well, and it's obvious where the error is".

You teach them something like returns in Python, or suggest they try Rust for a code challenge, and they start embracing the "expression" mindset and start making their code more railway oriented. It's hard, though, because both Go and Python encourage the imperative flow. Even JavaScript with it's Promises does.

So it's hard, but easy to fallback into a language that allows it and is built for it. Harder still to deviate when you're on a team that does it. Even in JavaScript, though, if you're on a team that embraces it, it's much easier, like say a newer React team that's fully onboard with Hooks & Lodash/Folktale, etc.

... that said, I whip out let when coding Elm, so not sure 😅...

Collapse
 
phlash profile image
Phil Ashby • Edited

I worked away from the code on modelling the problem to be solved, understanding the domain and the humans that do/will interact with the software.. Then on returning to the weeds, I had a much better view of why code should be structured in particular ways, and when procedural logic was appropriate (it often is for completing well-defined business tasks - do not throw process/procedures away!) or when a more flexible, isolated structured helped with uncertainty, at the cost of complexity of course :)

Collapse
 
jasterix profile image
Jasterix

This is a great and thoughtful approach. I will try spending more time away from the code, rather than just diving in. When working in a much larger codebase, how do you factor that in your solution design?

Collapse
 
phlash profile image
Phil Ashby

Welcome to re-engineering legacy products :)

My preferred approach, having got a decent understanding of where behaviour is well-defined, and where it might need to adapt, was to identify boundaries in the existing codebase/deployment (processes/VMs/etc.) architecture where the behaviour is contained (if anywhere!), then isolate those through application of the strangler pattern, to draw out a set of behaviours into a modular feature that can adapt more independently. If behaviour that requires change is very distributed, then we worked on drawing that together first, by applying facades / anti-corruption adapters in areas that connect to the behaviour, then making the changes to the behaviours as required (now in one place). This is an application of Kent Beck's 'first make the change easy, then make the change' :)

Collapse
 
sunflower profile image
sunflowerseed • Edited

Guess what, procedural is like actions... the physics, of particles flying around, or how they interact, due to some electrostatic force, and so forth.

When it is object oriented programming, it is like material, together with the actions. So you can say

particle.move()
Enter fullscreen mode Exit fullscreen mode

or

animal.giveSound()
Enter fullscreen mode Exit fullscreen mode

You are packaging the properties (states) of the material (the x, y coord, etc) together with all the actions it know to perform, into a specification, called a "class". The class is a blueprint, and when you instantiate the object, the object is born. It has properties, and it has all the "methods", or actions it know how to perform.

So the actions are still procedural.

In some situations, your "world" is just a number or a few numbers, and your just write everything procedural, because there is no need to "package" the states and actions into an object. Your program is the "world" and is the "object" already.

When you write programs, a lot of time you are concerned about the actions... and about packaging these actions together with the states into an object... that's not as important and it may depend on the situation. I saw people writing a function by write a class and all it does is one method, and so he either use it as a class method or instantiate the object and invoke obj.provideService() and it is kind of silly.

Note that the "action" is the method. In Smalltalk, you send the object a message, such as you tell the animal to giveSound, and the animal perform the giveSound action... so in some language, it is "sending the object a message". In some language, they call it "invoking a method on an object", and these are very similar ideas.

You mentioned when solving code challenges, you write it as a procedure. That's actually quite natural. For example, if given an array, and they tell you to write Quicksort, then you probably are not going to create a "Quicksort" object that can sort an array. You are writing the action, so while you can add a method to the array class such as array.quicksort(), you wouldn't usually write it as quicksort.sort(array) with quicksort being the object or even the class. You might write it as a library or module and use Sort.quicksort(array) with Sort being the class and quicksort and mergesort being the class methods. But it really depends on the situation. For many coding challenges, they want to see your algorithm skills, so it is natural to write it as procedural or functional.

Collapse
 
jasterix profile image
Jasterix

Thanks for the detailed response! You're right that may be the case with code challenges. But I noticed when working with my team (in an older codebase), it was challenging for me to write code within that context, especially when it to using fields and properties, extending other classes and working with arguments

I wish I could share the code as an example :(

Collapse
 
etienneburdet profile image
Etienne Burdet • Edited

I try more and more to start writing the higher level functions and/or top-level call first. So you force yourself into writing what your code should look like in the end, rather than where to start. And then you write the functions you would need in reverse order, writing the simplest, low level-utilities last rather than starting with state variable and mutating them little by little.

It's actually not that hard and I find myself writing much cleaner code from the start. If you want to test your higher level functions, just fake the lower level one to return basic string/fake object etc. before implementing real logic there. You might need to design stat beforehand too: pen and paper is still great for that :).

Collapse
 
jasterix profile image
Jasterix

thanks for the reply! Think I understand what you mean in terms of code challenges. But how would this work with a larger codebase with a language like c#?

Collapse
 
epsi profile image
E.R. Nurwijayadi • Edited

I don't try to change my habit.

I just find out that functional programming can reduce my code a lot. So I use whenever I can.