Refactor #3 – Designing a system Architecturally

When you look at refactoring some code there are a couple of reasons to do so. What are the reasons to do so? Take you pick from this list.
1. The version of Swift has changed, need to be current
2. I read about this amazing technique, I need to implement it
3. This would reorganize the code, make it simpler
4. This would remove the issues and reduce tech debt
5. I’d rather work on this code than take any other ticket

Well, all of the choices seem like the right choice, correct? Well depending on the maturity of your application and development process, yes they could all seem like valid reasons to refactor. However, the only acceptable reason is #4. Majority of the times it is only the reduction of tech debt that allows for code refactor.

Reducing Tech Debt

What kind of tech debt are we talking? Some developers let shoddy code slip into production and follow it up with “Yeah lets fix that when we consider fixing our tech debt”. Why does this happen? Is it just allowing shoddy code into production? Or is it not upgrading to a newer version of Swift so the codebase is now still running 4.1 where as we are talking about Swift 5.3 to be released soon. Due to time constraints, a majority of codebases suffer from duct-taping over duct-taping solutions. In addition there are several developers working on the same project and each that comes from a different background and varying levels of experience and maturity. We did look at that in the earlier articles regarding code smells. So developers that come from writing java code would write code differently than DotNet developers, than javascript developers. Depending on the tech that is in demand, a majority of developers spread themselves thin across several technologies. Now, some developers manage to do a good job of being multi-lingual and excel with a wide range of languages, whereas the average developer struggles even with a single language.

So what does all this have to do with Designing a System?

That is a good question. The reason why some development codebases get duct-taped, deeply coupled, littered with competing technologies and patterns, repetitive redundant functions, code smells and hundreds of cocoapods (literally) is… there is no design for the system. Because majority of the development starts with the assumption that Agile will help fix their code and automagically put it all together, so they can work on these little features and the features when complete will just connect together and work seamlessly as if these were Lego bricks.

Taking that segue, Lego bricks are build on a basic principle of studs and tube coupling. All pieces that are created follow the same principles. However a majority of codebases suffer, especially those that undergo several sprints and the transformational ideas of several developers in an Agile sprint window. To add to that, there are the added pressures of releasing half-baked code as the final product. So when different developers start with the codebase, they bring with them their developer odor (DO) and their own version of the Lego brick and more often they use a nail gun or hot glue to get the disparate pieces to stick together.

What would a recommendation be?

This is a very common issue, even if many would like to shy away from it and not admit that this exists. Again, the possibility of this happening in a smaller team is lesser. In larger teams and for larger organisations where several vendors work on the same app and developers change on the project.

The simplest, short recommendation to this is in the title of this article – “Design”.

I have seen a codebase at one of my clients, where the entire backend was modeled and created based on the front end. In practically 99% of the projects, there is a solid backend and plan of how things would work and then a front-end, essentially a user centric view is created to work with the API/Data. An unfortunate implementation of Agile, where everyone involved in this project were after only two things, consolidating their position and progressing it. So developers with varying levels of maturity moved on to take up roles like Scrum masters, Po’s, Team leads, etc. Including roles like solution architects and business analysts. The project had deliverables so it was an app in the market. However, it had the worst ratings ever, literally 1 – 2 stars. It had become a mess that had several patterns embedded and code smells. Some developer thought that *magic* navigation could be achieved if they used RX and another thought that with RX they can have a mainStore with each and every model stored in this global singleton.

The reason for this failure was that there was No Design. Most of the discussions happened verbally and no one ever tried to even use confluence to document what they were planning to do and have buy ins. Then came the second wave, when they recognized the problem that the codebase is too tightly coupled and needs to be de-coupled. However again starting greenfield is not an options as if “this codebase took a couple of years, greenfield would take similar to reach a level of maturity”, right? (Verbatim words of the stakeholders). They could not afford to take a risk so they went on butchering the code further adding to it complexities that are unimaginable.

What kind? Tell me, I have popcorn 🍿

This is interesting, glad you have some popcorn. I’d list some of those than a paragraph

1. Include Reachability as a cocopod

It’s nice to know if you have connectivity or not, however the butchering comes by adding Reachability as a Pod and then creating a custom class called Reachability, the same as the Pod and then create a custom interface that has exactly different functions. So switching back to the Pod version is simply impossible.

2. Do not create a wrapper, but instead use the API/code call directly.

Nice to create some functionality and then include it in literally hundreds of the swift classes directly, so now if you want to switch it off you have to go and change each of those files. Alternatively something that I prefer to use since long is have a wrapper function, (Pop or rock is not preffered🤣) For example, you want to display something to the console, you can use print(“message”) but then when you send this to production it still prints to the console information that it should not. What I do is have a wrapper function called func consolePrint(_ string: String) that is defined as follows

func consolePrint(_ string: String) {
 #if DEBUG
    print(string)
 #endif
}

this way it will work only while debugging, (Mine is a bit more complex and has more bells and whistles) but you get the idea.
Not creating a wrapper means rummaging though the entire codebase and removing each and every print statement. Scale this up to think of network calls, etc

3. Using Magic 🎩🧙🏼‍♂️

This is not about pulling rabbits out of hats or burning paper to turn them into doves. This is about trying to pass on the management of events and others to React. Nothing wrong in using react, but do not use it for magic, where you don’t want to write native code. It’s difficult to cite an example other than simply saying that don’t mix your tech.

4. Fallback to WebViews

In all honesty, I am in awe of the fact that even in the early 2000’s when majority of the development was on Windows boxes and IE was the browser of mass preference. You could have some amazing speed and animation using DHTML and JavaScript/VBScript. While at the same time, desktop versions of the applications written in VC++ or VB (not DotNet) were not as performant. It kind of just blew my mind. Even today there are some amazing interfaces that can be created using HTML that native still struggle with. Having said that, it is not exactly a good idea to mix and match as it imposes other issues. I recollect one instance when we had a Swift 2.0 project that used a WebView and the reviewers at Apple rejected the app and send us screenshots of the app with images of puppies in the WebView instead of it showing the help/technical details. This was an eye-opener and embarrassing at the same time. Another codebase reminds me where due to time constraints, a webview was used for a highly custom UI because meeting the deadlines was cutting too close. Creating the same with native code would just not work. The advantage was that the HTML could continue to be worked on while Apple was reviewing the application. In those days it took north of 2 weeks to approve an app. Whatever the advantages, it is easier to have a singular interface, go Native or go web wrapper. The hybrid will always bite you in the butt.

5. Design Pattern

This is a controversial one, what design pattern should you use? MVC, MVVM, MVP, VIPER, etc I would not even go into recommending one over the other. Each serve a purpose and as they say “Whatever floats your boat”. However the problem is when a particular pattern is used, rather misused due to lack of understanding or using it in a way it was not meant to be used. A very old instance comes to mind from the mid 90’s where I recollect in my dad’s office this lady would use Excel to write letters and use Word with Tables to create spreadsheets. Visually she could see tables in Word where as in Excel she saw columns and it made more sense to her to use those to write a letter and format the text accordingly. It worked for her but it did not make sense to me then and even now.
The problem is when developers start with things like separation of concern, separating the View and the Model, reducing the controllers into smaller chunks. They end up simply moving the code into another file. It’s like sweeping the house and putting all the dust under the carpet. The S in SOLID that stands for Separation of concern gets used as S for Shifting.

There are more we will explore in later articles

What does Design help in? That’s still unclear

So far we have outlined the issues and the problems in which there is another major problem which is not technology based but nevertheless introduced with Agile. Scrum masters either take ownership or simply disappear into the background and let the teams fight it out. Both come with their own issues, the ones that take ownership are good as long as they don’t become the dictators to the point where they even squeeze the estimates as I would take half a day, why do you need 2 for that. The ones that disappear are equally bad as instead of facilitating the discussions they simply step out and let the agressive, passive aggressive members of the team battle it out. These are both situations where teams start to fall apart and developers leave, things change and codebases get butchered.

A design (Architecture) is the diagram of the roadmap, where the roadmap is a document that the stakeholders use to determine how and when things are going to get done. The Design is the document the developers use to determine what are the core functionalities and how they talk to other components and how they would grow. This even outlays the interactions between the tech stack and external systems. Drilling down it would go into UMLs and other documents that detail how the components would work. The lack of this document empowers (in a negative way) the developers to take an approach that they feel which can be in a totally opposite direction to the current devs and/or other previous devs. Which ultimately results in code that can result in SNOT (in Lego terms, Studs Not On Top. Traditionally Lego are all built with the studs on the top, connecting/stacking the pieces on top of the other). This can in the case of Lego result in some exciting results, but with Software more often than not is a disaster.

Summary

In summary, the takeaways from this is
1. Do not Mix and Match your patterns
2. Have a design pattern at the start
3. Consistency across the codebase
4. Address TechDebt intelligently modifying code according to the reason for the Tech Debt reduction
5. Synchronize Agile for everyone

Views All Time
Views All Time
535
Views Today
Views Today
1
Posted in Uncategorized.