How to improve your UI5 app code quality and maintainability
Introduction
I'm a consultant, which means I go from different code bases on a regular basis and this list of steps comes from the processes that I employ at all of my clients.
The most common problems that I run into is that of massive controllers which is something I've heard others complain about online/ via the OpenUI5 slack community.
In this blog I'm simply going to lay out a number of things that I do to deal with this problem and how you can too, they're realistically common practices across all of software development with a few UI5 specific examples.
Use Git
Git is the standard in the industry for version control. You are about to embark on improving your UI5 application which will often mean deleting code, moving code or re-writing code and if you don't understand the importance of version control then you should go and read about Git and how it can change your life.
The only way that I am ever comfortable with deleting files, functions and re-writing code from a legacy project is the knowledge that in some form I will have a copy of the code at each step along the way.
If you take just 1 thing from this blog it should be the idea that you need to use Git in all of your projects.
Delete unused functions/ dead code
This one is really easy! Go through every single function title and search for it's reference inside of the entire application. Take important note that it could be called from other controllers or from the view directly and as such you need to make sure that you just search in your entire project structure.
Found something that you can't see is used anywhere? delete it. Just because you delete it that doesn't mean it's gone forever as you should be using Git for all of your projects and so a copy of this function will always exist. Perhaps write in your commit message the name of the function deleted for easy searching later, but there is often so much dead code on legacy projects because people are afraid to delete it.
Give your functions / variables clear and understandable names
Functions and variables need to have good names. I would always take a function named "returnUserNameFromSettingsModel" over say "returnUN" as contextually "UN" might make sense but the former is much more specific and less ambigious. I understand this example is a bit contrived but this is a common pattern that I see afflicting applications.
Give full and clear function and variable names and it will help you in the long run as while you might understand the names today someone else even you might fail to remember them a week from now.
Don't repeat yourself
Don't repeat yourself (aka DRY) is a common software engineering principle and one that should be easy to implement. Look at your functions, do you have a number of common and repeating steps? For example let's say that you have a step to refresh your table data.
You might do this step when you delete an item from that table, add an item, update an item? Maybe you just refresh the table through a button click? Whatever the case you will probably have repeated that same 'refresh table' code multiple times throughout your application so you need to extract this method into a single function and call that rather than repeat the same code over and over again.
Not only will this improve your code quality, maintainability and readability it will also reduce the number of lines of code and make our life easier.
Make use of your base controller
Larger applications (multiple views and controllers) should make use of a base controller, this is commonly found inside of the larger SAP template applications and so shouldn't be that hard to introduce if you're missing one. (let me know if you'd like a blog on this subject)
The base controller can then be used to store common functions which are used across your application, this is typically little helper methods such as 'return currently logged in user' or 'return odatamodel' as seen in my promises blog series.
This is essentially an extension of DRY but making use of our tools at our disposal such as the basecontroller as this is common among our applications.
Do note that this hint/ tip can easily spin out of control and leave your base controller with 2K+ lines so you should only use when appropriate.
Refactoring
Refactoring is the process of taking existing code and improving upon it by re-writing existing code to produce the same functionality. This is often written in software development as 'altering its internal structure without changing its external behavior'. This is something I'm doing constantly with my code and my projects and should be the same for you too.
Do not just sit down and refactor once, the act of refactoring is a discipline and a continuous process to look at something and think 'can I do this better' which is something we should all strive towards.
There are many blogs, books and videos on the subject of refactoring so I won't re-hash everything that's been said elsewhere. Refactoring is one of the most important processes a software developer can integrate into their workflow regardless of the programming language or framework used.
Make utility files
Sometimes I make utility files, a utility file is where I extract out common processes and throw all the functions for that into a single file. There are examples such as instantiating fragments and place them into a 'fragmentManager' file. I can then use this file as the place where I instantiate my fragments, so to access the functions I need to simply import the fragment manager into my controller.
This can go a long way to reducing almost 'boilerplate' code in our controllers or even base controller and place it in a nice little box where we can call them as needed. This has the same benefits as moving items to the base controller but becomes more specific and helps us reduce base controller bloat if that starts to get too unwieldy.
Direct model binding
While I fully admit to and make use of JSONModels on a regular basis and that their use is sometimes unavoidable you should/ make use of direct model binding wherever you can.
Direct model binding is where the call to your entity set/ service is done inside of your XML and contains no JavaScript at all. This is most often achieved with controls such as search helps because they're usually only returning lists of data with no dynamic filters.
This can free up your controller immensely if you have lots of calls that can easily live in the XML and automatically update with your gateway service.
communicate with your gateway developer
This one admittedly isn't one that works very well if you're inheriting a legacy application because you might not have much time for a gateway developer. However let's assume that you're able to work with a gateway developer and make some changes.
You need to identify areas of your application where UI5 is doing lots of heavy data lifting (transforming data, nested data calls, sorting and filtering) and see which of these activities are ultimately better suited to the gateway. Not everything can be moved to the gateway but there are certain activities that should exist on the gateway and can be more easily implemented vs doing more work on the front end.
There might be a good reason this was done in the UI in the first instance, so this really requires you to tackle each item with common sense. Don't force this work to the backend developer if it's harder for them to implement, but if it's something that's easier or is heavy data lifting try and have a conversation about where the functionality is completed.
Conclusion
These were the main steps that I undertake when I join a new project and inherit legacy applications as well as during my own application development outside of clients. This definitely isn't everything that you could possibly do (I didn't even touch on testing) but feel free to leave your hints/ tips in the comments down below!
···