because making even seemingly small and simple changes to the old version of the shop was extremely difficult and costly in terms of engineering time

?

Are the people who built browse bad developers?

NO!

// writing code is the easy bit

ADDING FEATURES IS THE HARD BIT

OK, we've got the first version of browse shipped! 🎉

OK, we've got the first version of browse shipped! 🎉

Now we're adding Essentials

OK, we've got the first version of browse shipped! 🎉

Now we're adding Essentials

Let's add the "SALE" pill

and before you know it...

computers are fantastic at understanding code

 

humans...less so

writing code that computers understand is the easy part

writing code that humans understand is the hard part

const newFiltersetToUrl = ({
  newFilterset,
  categoryApplicableFilters,
  oldMatchParams = {},
  alwaysIncludeBaseUrl = false,
}) => {
  const newUrlOfQueryParams = parseFilterSetToShopQueryParams(newFilterset);
  /* see if we have just one top level category requiredId
   * in which case we'll ditch it as a requiredID param
   * and take the user to the nice URL for that category
   */
  const { fromId } = whitelistedUrlsForCategories(categoryApplicableFilters);

  const requiredIdsThatAreCategories = Array.from(
    newFilterset.requiredIds
  ).filter(id => fromId.has(id));

  const hadBrowseList = oldMatchParams.browseListId;
  const newFiltersetHasBrowseList = newFilterset.requiredIds.has(
    oldMatchParams.browseListId
  );

  const shouldSwapToNiceCategoryUrl =
    newFiltersetHasBrowseList === false &&
    requiredIdsThatAreCategories.length === 1;

  const generateUrlToReturn = () => {
    if (shouldSwapToNiceCategoryUrl) {
      return generateUrlForMatchedCategory({
        filterset: newFilterset,
        fromId,
        categoryId: requiredIdsThatAreCategories[0],
      });
    } else if (hadBrowseList && newFiltersetHasBrowseList) {
      return generateUrlForBrowseList({
        filterset: newFilterset,
        browseListId: oldMatchParams.browseListId,
      });
    } else if (oldMatchParams.niceUrl || oldMatchParams.browseListId) {
      // we had a nice URL before and now we don't
      // so we need to put the baseUrl in here
      return baseUrl + newUrlOfQueryParams;
    } else {
      /* but if we're here and are not sure how to deal with the URL change
       * in a way that maintains the "nice" URLs
       * let's just use query params, which will work totally fine
       */
      return newUrlOfQueryParams;
    }
  };

  const url = generateUrlToReturn();

  if (url.includes(baseUrl) || alwaysIncludeBaseUrl === false) {
    return url;
  } else {
    return baseUrl + url;
  }
};

const generateUrlForBrowseList = ({ filterset, browseListId }) => {
  const newUrlForBrowseList = urlForBrowseList(browseListId);
  const filtersetWithBrowseListRemoved = mapFilterset(filterset, filterset => {
    filterset.requiredIds = filterSet(
      filterset.requiredIds,
      id => id !== browseListId
    );
    return filterset;
  });
  const newQueryParams = parseFilterSetToShopQueryParams(
    filtersetWithBrowseListRemoved
  );

  return newUrlForBrowseList + (newQueryParams === '?' ? '' : newQueryParams);
};

const generateUrlForMatchedCategory = ({ filterset, fromId, categoryId }) => {
  // take the user to the nice URL for this category
  const newUrlForCategory = baseUrl + fromId.get(categoryId);

  // now we remove that requiredId
  const filtersetWithCategoryIdRemoved = mapFilterset(filterset, filterset => {
    return {
      ...filterset,
      requiredIds: filterSet(filterset.requiredIds, id => id !== categoryId),
    };
  });
  const newQueryParams = parseFilterSetToShopQueryParams(
    filtersetWithCategoryIdRemoved
  );

  return newUrlForCategory + (newQueryParams === '?' ? '' : newQueryParams);
};

export default newFiltersetToUrl;

Tear it down and start again

time

ground up rewrite

value added

time to reach feature parity

shipped and building new features

?

So how do we avoid constant rewrites?

time

incremental improvements

value added

incremental improvements

and feature shipping

time

incremental improvements

value added

incremental improvements

and feature shipping

feature parity + then new features

Writing code with the future in mind

We cannot predict the future

this isn't how it normally goes!

we just need to quickly add feature X in time for the next sale

0.5 days

2 days

so this is better for the code...but is it better for the company?

Uncomfortably Fast

Tech work and investigation

This tension is good!

TECH
DEBT

Cutting corners to let us ship today

TECH
DEBT

SHIPPING
PACE

TECH
DEBT

SHIPPING
PACE

TECH
DEBT

SHIPPING
PACE

TECH
DEBT

SHIPPING
PACE

Time to rewrite!

How do we avoid getting to this stage?

knowing when to take the time to place these bricks is hard

Past Jack

Present Jack

Future Jack

(Well, Jack's dad)

Past Jack

Present Jack

Future Jack

(Well, Jack's dad)

Past Jack makes a lot of mistakes

Past Jack

Present Jack

Future Jack

(Well, Jack's dad)

Present Jack learns from those mistakes

Past Jack

Present Jack

Future Jack

(Well, Jack's dad)

to hopefully make Future Jack's life easier

Was that decision I made on the shop the right one?

Where am I?!

Q&A?

Shop L&L Thread

By Jack Franklin