Rebuilding and Refactoring Bootcamp Projects
Tags: javascript, webdev
Published: 2021-08-29 22:27:05

Since graduating from the coding bootcamp I attended, I've started working as a grading TA for said bootcamp. I'm currently grading for a beginner course. In this course students learn the basics of JavaScript: conditionals, loops, functions, objects, DOM, local storage. It's honestly a very humbling experience to look back at these projects I built myself 7 months ago and recall how hard they were (tbt to crying over nested for loops). 

In fact, it has inspired me to rebuild these projects without looking at my old code and without looking at any starter code. Just me, my brain, and a quick google search if necessary. 

If you are a bootcamp grad, I highly recommend trying this! It's honestly so fun. It's like traveling back in time but you have the skills and abilities you wish you'd had then. You're able to build projects that took you a week in an evening, and you're able to incorporate functionality and best practices to make your code clean and readable. 

For anyone that's gone through a bootcamp, we all know how fast they move. I personally never had the time to refactor my code because by the time I had reached MVP at 10pm for my project that day (A) my brain was fried or (B) I still had more homework to do before class the next morning.

## Enter Monster Functions

This left me with what I'm going to refer to as 'monster functions'. Monster functions are BIG, usually 15+ lines of code that hold 3+ layers of logic. They are hard to test and make debugging more difficult than it needs to be. You can't tell exactly where your script is crashing inside the function because of all the layers of logic. 

Last night, I was working on a sort of right-of-passage project from my bootcamp called Salmon Cookies. The scope is you're helping an owner keep track of the cookie sales of all of his shops via a table ([see old repo](https://github.com/LydiaMT/cookie-stand)). Simple ask, but wow did this throw all of us on week 3 of learning how to code. The following image is basically the end goal:

![Table of cookie sales. Header row captures Shops, hours from 6am-7p, and Daily Total. The following 5 rows are cookie store names Seattle, Tokyo, Dubai, Paris, Lima with cookie totals each hour, and the total cookie sales for the day as the final td in the tr. The footer shows hourly totals and has the sums per hour for all the stores](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/90fe5e8k3ryymycpav29.png)

So last night, I'm rebuilding this project for fun (Saturday night #turnup). The final feature they had us incorporate was a form where you can add new stores to the table. I get it to work, but I have created a monster function 🧟‍♀️. It's 30 lines of code and is doing too much. No need to read through it, just bask in the glory of this  thicc function.  

```javascript
function makeStore(event){
  event.preventDefault();
  let storeName = event.target.storeName.value;
  for(let j = 0; j < stores.length; j++){ // Check to make sure store isn't already on table
    if (stores[j].name.toLowerCase() === storeName.toLowerCase()){
      alert("This stores metrics have already been calculated");
      return;
    }
  }
  let minCustomers = event.target.minCustomers.value;
  let maxCustomers = event.target.maxCustomers.value;
  let cookiesPerHr = event.target.cookiesPerHr.value;
  let newStore = new Store(storeName, minCustomers, maxCustomers, cookiesPerHr);
  stores.push(newStore);
  let newStoreColumn = document.createElement('tr');
  parentBody.append(newStoreColumn);
  let newStoreColumnTitle = document.createElement('td');
  newStoreColumnTitle.textContent = newStore.name;
  newStoreColumn.append(newStoreColumnTitle);
  let total = 0; 
  for(let i = 0; i < newStore.hourlySales.length; i++){
    let newCookieData = document.createElement('td');
    newCookieData.textContent = newStore.hourlySales[i];
    newStoreColumn.append(newCookieData);
    total +=  newStore.hourlySales[i];
  }
  masterTotal += total;
  let dailyTotal = document.createElement('td');
  dailyTotal.textContent = total;
  newStoreColumn.append(dailyTotal);
  makeNewTableFooter();
}
inputData.addEventListener('submit', makeStore)
``` 
Wowzer 😅. So I'm sitting there going 'cool it works!', but there's that voice of reason inside my head that's like 'bruh...refactor this'. That's the beauty of revisiting these old projects after you graduate. You finally have the time and energy to deep dive into these problems. So I break it into chunks:

1. I'm gathering user input from the form to make a new store object.
1. I'm checking for duplicates on my table as to not add the same store twice.
1. I'm building a lot of DOM elements and appending them. Let's just make that it's own helper function.
1. Once I've created a new store, I'm adding that store to the table. 

I ended up breaking this into 4 individual functions. Plus, the `makeElementAndAppend()` function ended up being a fantastic utility function that I was able to use in other parts of my project. 

``` javaScript
// ==================== DOM HELPER FUNCTION ====================
function makeElementAndAppend(element, parent, attributes = {}){
  const e = document.createElement(element);
  for(const [key, value] of Object.entries(attributes)) {
    e[key] = value;
  }
  parent.append(e);
  return e;
}

// ==================== GATHER USER INPUT FROM FORM ====================
function getStore(event){
  event.preventDefault();
  let storeName = event.target.storeName.value;
  if(isDuplicate(storeName)) return; // Dup check
  let minCustomers = event.target.minCustomers.value;
  let maxCustomers = event.target.maxCustomers.value;
  let cookiesPerHr = event.target.cookiesPerHr.value;
  let newStore = new Store(storeName, minCustomers, maxCustomers, cookiesPerHr);
  stores.push(newStore); 
  addStoreToTable(storeName, newStore); // Add to table
}
inputData.addEventListener('submit', getStore);

// ==================== ADD NEW STORE TO TABLE ====================
function addStoreToTable(storeName, newStore){
  let newStoreColumn = makeElementAndAppend('tr', parentBody); // make new row
  makeElementAndAppend('td', newStoreColumn, { textContent: storeName }); // make shop name first td in new row
  let total = 0; 
  for(let i = 0; i < newStore.hourlySales.length; i++){
    makeElementAndAppend('td', newStoreColumn, { textContent: newStore.hourlySales[i] });
    total +=  newStore.hourlySales[i];
  }
  masterTotal += total;
  makeElementAndAppend('td', newStoreColumn, { textContent : total });
  makeNewTableFooter();
}

// ==================== DUP CHECK ====================
function isDuplicate(storeName){
  for(let j = 0; j < stores.length; j++){ 
    if (stores[j].name.toLowerCase() === storeName.toLowerCase()){
      alert("𝙏𝙝𝙞𝙨 𝙨𝙩𝙤𝙧𝙚𝙨 𝙢𝙚𝙩𝙧𝙞𝙘𝙨 𝙝𝙖𝙫𝙚 𝙖𝙡𝙧𝙚𝙖𝙙𝙮 𝙗𝙚𝙚𝙣 𝙘𝙖𝙡𝙘𝙪𝙡𝙖𝙩𝙚𝙙 ✅🍪 ");
      return true;
    }
  }
}
```
I do not doubt that this could be refactored more, but for a silly-side-practice project this felt sufficient.

## In Summary

In the past I remember shying away from refactoring because I wasn't sure how to use variables from one function in another function, and I didn't want to break my code. Just remember, you can *literally* pass variables you're using in one function to another function! I recall months ago not grasping how I could use `something[i]` outside of it's own for loop. However, you can just pass it into your function invocation such as `makeElementAndAppend('td', newStoreColumn, { textContent: newStore.hourlySales[i] })` by calling that function within the loop. That is probably incredibly obvious to some people, but for some reason that just recently clicked for me. 

**My takeaways from this experience:**

- Isolate logic and make each piece of logic its own function.
- Create utility functions when possible for repeated logic.
- Remember that you can pass variables you're using in one function to another function. 
- It's ok to start with a monster function to get all your logic on 'paper'. Start with a huge function and then practice breaking it apart.