Re-factor-ing

Hocky Yudhiono
9 min readMar 23, 2022

--

When you code, sometimes you code too much that you don’t understand any lines of your codes anymore. You’ll be confused of what is what by now, only small comments are left a little algorithm idea, and a whole ton lots of nasty spaghetti database for the rest of the .tsx of your projects.

So what now? should you take your time to read and understand the code, or should you rewrite the code? or should you refactor the code? 🤔

Refactoring is a way to make code more readable, easy to modify, and concise. Somehow, when you code you’ll need a bigger plate to serve the customer, more feature and more stuffs to give to your costumer. But your codebase is only prepared for one person. It’s time for a refactor.

“We improve the code where we work, and ignore the code where we don’t have to work. Odds are, we’ll visit this place again.

“Lol just read and patch codes, if it’s all running no need to refactor” You think so?

First things first, refactoring sometimes can be stupid. There are several easy refactorings like renaming files, moving files, creating new components that can broke your code.

So you may use a refactoring tool, a weapon to start your refactoring, so it can be easier. Me myself, I love to use IntelliJ Idea. It has lots of cool feature, like:

  • Rename imports when removing, or renaming files.
  • Autocomplete and auto imports after typing a module, library, constants or variables.
  • Easy to set up linter that interpret the code on the run, and show an easy fix in the code.

Other IDE like Visual Studio Code or Eclipse can also be used. Just make sure your tools are be able to be used to refactor your code and maximize your productivity!

Next, you can use external third party like sonar qube.

Apps like sonarqube can be used to detect code smells, checking vulnerabilites and security issues, automatically. Apps like these are this are dedicated apps made to detect flaws in your applications. It’s good, because humans did mistakes and programs are to be made just so it’s easier to see something that humans can’t!

Excess is Recess

You refactor to remove excess code, so it will be easier to understand and modify. If in a day you’re adding 20 lines, In a week there will be 100 lines. In a month there will be 400 lines. Now you think you shouldn’t maybe make a new folder? at least? rename your codes, put it into the appropriate folders? 😡

Human is a Social Creature

By refactoring code, you can better understand others code. Now, let’s get into my team’s software engineering project, the cryptocurrency all-in-one app’s codebase. I’ll check my friend’s, Lo and behold, the code of Eko lol, just like short piece of the code.

// custom secure store & load function
// default to SecureStore and fallback to AsyncStorage
const secureStore = async (key, value) => {
if (await SecureStore.isAvailableAsync()) {
await SecureStore.setItemAsync(key, value);
} else {
await store(key, value);
}
};

const secureLoad = async (key) => {
if (await SecureStore.isAvailableAsync()) {
return await SecureStore.getItemAsync(key);
} else {
return load(key);
}
};

Yes, the code was short and it is kinda readable, thanks to the good naming function. But what if the code was like

const scst = async (k, v) => {
if (await SS.avail()) {
await SS.setAsync(k, v);
} else {
await st(k, v);
}
};

const scld = async (k) => {
if (await SS.avail()) {
return await SS.getAsync(key);
} else {
return ld(key);
}
};

Okay, so this is just horrendous. And yes, if you think refactoring is closely related to clean code, the answer is Yes. Absolutely.

You refactor code to remove code smells, and to imply clean code. That’s why I was kinda confused why the grading indicator of the course I’m currently taking separate both of these things, which I thing it shouldn’t be done.

Wait what does this code do?

Have you ever see your old code and wonder how it works?

const HalfPageLogo = ({ children }: { children: ReactNode }) => {
const windowWidth = Dimensions.get("window").width;
const windowHeight = Dimensions.get("window").height;

const percentage = 0.4;

return (
<VStack flex={1}>
<Flex
marginTop={
-windowWidth +
(windowHeight < (1 / percentage) * windowWidth ? -1 : 1) *
(windowWidth - windowHeight * percentage)
}
width={windowWidth}
height={windowWidth * 2}
>
<AjaibLogin testID="ajaib-logo" />
</Flex>
<BasicContainer>{children}</BasicContainer>
</VStack>
);
};

I wonder who wrote this code in our repo. Yes, the one and only Hocky Yudhiono. I wrote this code 2 weeks ago and I already forgot why it was like this.

But anyway, it’s not that important. Because for now, we don’t need the feel to have this refactored, because it’s still just one line and you can still grasp what the magic formula does. I think it should be refactored like this in my next commit:

// The formula is computed to align the logo exactly on the percentage height of the windows height.
.
.

No brain load

Let’s say there is Venti, a new guy who is joining the team, when he onboards the team, he should learn the code and know what it does. So, if he is faced with a good code base, he shall have a good insight on what the code does, and he can start to work immediately.

Code Smells

Refactoring is done basically to remove code smells, or just to prepare extensions for future code. Because you know, we’re developing in an agile environment, the core codes like the connector, the navigator, the app layout base should be coded carefully as it will be reused everytime in the future. You should prepare for the worse when coding these kinds of important things.

Or at least, just give an idea on how to scale it on the comments. Next time you’re going to do it, you’ll be ready.

In react native, there are several code smells you can encounter more than other frameworks.

Too many properties

Passing too many properties into a single component can be crazy.

export const MnemonicButton = ({
prop,
isClickable,
addToBox,
}: {
prop: string;
isClickable: boolean;
addToBox: () => void;
}) => (
<Button
isDisabled={!isClickable}

Welp, in my project, there is nothing really bad happening yet, we all keep it under three still, but in the future if your code has too many props, it’s a sign for you to refactor it. One of the solution is to check whether you can remove any props or obtain one from the others, or just like do a component decomposition, etc.

You don’t really wanna pass too many unused props as well, if possible use the props option pattern and make it optional.

Props into state

some more code smells in beginner is like making a props and turning it into a state. Just don’t. If necessary, pass the whole state into the props, just don’t create another intermediate state, because that is just bad.

If you need a loop inside those return that can’t be inlined?

You really should need refactoring.

function Component() {
const what = () => {
components = []
// for loop and push to components tedious implementation
// .
// .
return components;
}
return (
<div>
{topSection()}
</div>
)
}

consider moving it into some several components.

You do realize that 32 boolean makes an int32 right?

function Component() {
const [isLoading, setIsLoading] = useState(false)
const [isFinished, setIsFinished] = useState(false)
const [hasError, setHasError] = useState(false)

const fetchSomething = () => {}

return <button onClick={fetchSomething} />
}

Let’s say you have some several boolean states, if this the case, you should refactor it and use an integer marking as set or just use string instead. Those data types changing won’t cost you as much load as making 3 boolean states.

UseEffect is Evil

too much useeffect hooks in your code can slow down the performance of your code.

Our project has this issue, the register confirmation screen is so lag, just because we want to check whether a button is ready to be enabled or not.

useEffect(() => {
let isSame = true;
const newClickableMnemonics = Array(mnemonics.length).fill(true);
if (mnemonics.length != mnemonicsBox.length) isSame = false;
else {
for (let i = 0; i < mnemonics.length; i++) {
if (mnemonics[i] != mnemonicsBox[i]) {
isSame = false;
break;
}
}
}
for (let j = 0; j < randomedMnemonics.length; j++) {
for (let i = 0; i < mnemonicsBox.length; i++) {
if (randomedMnemonics[j] == mnemonicsBox[i]) {
newClickableMnemonics[j] = false;
break;
}
}
}
setConfirmable(isSame);
setClickableMnemonics(newClickableMnemonics);
}, [mnemonicsBox]);

this is just evil. You should refactor your code to use an appropriate data structure, usestate design pattern, and for heavy computations, use the useMemo hooks instead. You really should avoid this useEffect as much as possible because it’s really heavy. Moreover if the dependency variable is really prone to change. Everytime it rerenders, it will be computed once or twice more! Just crazy.

In a coding environment, refactoring is not something to be done “rushingly” You should know when to and not to refactor.

When to Refactor

  • One should refactor after fixing a bug or changing a functionality, refactoring doesn’t mean changing how a code works, but making a better structure with the same functionality. Refactoring means creating a new way for your code to flow nicely. Of course, code is something that is working on the inside part of our box (black box point of view), and the end users wouldn’t see what’s working on the inside. 🧼
  • If you think the change will improve the design of the code, You should do it, refactor sometimes can improve the speed and performance of the code. So it might not be bad to do it.
  • If you think the change will improve the readability of the code for other developers, you should do it.
  • Improving algorithm sometimes takes brains power, you should learn complexity analysis, both memory and time complexity analysis. For example, data structures can be utilized to improve the speed of parts of your code.When refactoring, you should also consider with everyone whether it’s okay to use a new library or module, or just implement it by yourself. Data structures like segment trees, trie, fenwick tree, and other heavy algorithm dependent data structures needs a reliable algorithm coder to implement. If you’re not so sure, you can use external libraries. But keep in mind that these codes needs to be maintained actively by the repository owner. And yes, it is possible for your team to fork the library. Also, you should consider the active licensing and copyright of the code!

When Not to Refactor

  • When you’re working for a feature, and doing other parts of work, don’t refactor in the same branch.
  • Never refactor alone, by yourself. You should ask for someone to at least do a pair programming or a thorough review for your refactor. The second person will definitely ensure that code are being made easier to understand and not making it worse.
  • Making a small commits, and checking tests after a small change can make refactoring easier to debug. That’s why you shouldn’t refactor after you finish making tests.
  • When you find or spot a code smell, one should remember and take not of it. Finish the features and other important stuffs, make sure there are tests, then commit. After that, you may refactor the code.

References:

--

--

Hocky Yudhiono

Fullstack Developer 💻, Competitive Programmer 🐭, Blogger 📝, Eager Learner 💤 | https://hocky.id/