The Latest

The latest posts listed chronologically

View posts as grid
February 28, 2024

Code is messy, language is messy, and sometimes you want to rename a model in a codebase that a team has sunk years into...for consistency's sake.

Once a codebase hits a certain size or has multiple active participants, it becomes unrealistic to make sweeping changes like these. These types of sweeping changes inevitably run into problems. An all of nothing approach on this scale can lead to hefty merge conflicts, a massive PR that is hard to review and test, or a feature branch that starts diverging heavily from the main trunk and becomes hard to maintain.

A more manageable approach to a large change like this, is to break it into smaller pieces that can merge into the main trunk while keeping the project in a working state.

Doing a quick find and replace isn't enough in this case, as there are some tricky places our model's name might live.

  • Database relationship columns may be referencing the model name, think {model}_id, user_id, team_id etc.

    • Quick Tip: If you're using MySQL, you can find all tables referencing the model's name in your database using the following query. SELECT table_name, column_name FROM information_schema.columns WHERE column_name LIKE '%model_name%;
  • Database indexes may be referencing the same fields.
  • Polymorphic relationships in the database may be referencing the model name.
  • The model may be serialized in the database or used in our queues.

In this example, we'll be renaming a Team model to Organization in a Laravel codebase.

To keep the project in a working state, while introducing the new name, we'll take advantage of a PHP built in function, class_alias. This will allow both the old model name and the new one to be used in parallel.

Add an alias

We could use the class_alias function directly, in somewhere like the boot method of our AppServiceProvider, but Laravel provides a way to add aliases in the app config. So we'll stick to conventions here.

In configs/app.php, look for aliases and add an alias from the old model to the new model. It should look something like this.

'aliases' => Facade::defaultAliases()->merge([
    ...
    'App\Models\Team' => 'App\Models\Organization',  
])->toArray(),

This is where Laravel is loading all of the facades. Under the hood, it is using the PHP function, class_alias.

Update the model

Rename the Team model to Organization — both the file and class name.

We aren't writing any migrations today and to keep the code running, we need to override some of the Laravel auto table name and foreign key features. We want the model's table name to point to the existing table and relationships to use the existing columns in our database.

Add the table property to the model.

protected $table = 'teams';

Override the default getForeignKey function in the model.

public function getForeignKey()
{
    return 'team_id';
}

Update morph maps

Morph maps are aliases used to reference a class in our database without using the actual class path in the database. This could mean using 'Team' instead of 'App\Models\Team'. You'll most commonly see these used for polymorphic relationships but they could be used for other things as well.

We need to allow the value that is used in the database to remain the same while mapping to our new model.

In the morph map, change from: 'Team' => Team::class to: 'Team' => Organization::class.

Relation::enforceMorphMap([
    'Team' => Organization::class`
])

Both (new Team())->getMorphClass() and (new Organization())->getMorphClass() should now return 'Team'.

Using the alias

After these steps, our app should be in a working state, only now, we can use both model names interchangeably. References to both Team and Organization will work.

From here we can chip away at updating our codebase and database to use the new name in bite sized bits. This lets us keep the codebase moving in the direction we want to go, without putting a stop to everything else.

The downside of course, is that we now have two ways to reference the same model, making things a little confusing in the interim. So don't sleep on the rest of the work.

Consistency...here we come.

December 31, 2021

Pack the car. Fill up on gas. Grab ice from the store. And hit the road.

The getting away part of getaways is the hardest part. We were headed to Sequoia National Park from Los Angeles. The drive to our campsite is an easy 4 hours. Take the 5 to the 99, and drive a winding road into the mountains. It should be an uneventful drive through California's Central Valley.

Shortly after taking the 99, Google Maps suggested that we get off and take a two-lane road the rest of the way. It's the quickest route possible, shaving off a few minutes. Still in our city life mindset — we obliged. The road made its way through central valley farmland. It was relatively fast going, we drove over 60mph the whole way. Cars bunched together from one car going a little slow and nowhere to allow cars to pass. Cars coming the other way were frequent with semi-trucks intermixed. The air from passing trucks was powerful enough to swerve the car and I quickly learned to drift right as cars were coming.

The lineup of cars we were in was long. There was some space between me and the cars on either side but not in abundance. Drought-stricken California left no green to be seen and made me ponder The Dust Bowl. Tractors and trucks worked to prepare empty dirt fields. We approached one such dirt field with a truck dragging something through the field. Dust billowed in a trail behind it. The breeze blew the dust the few yards it took to cross the fence and enter the road. It looked ominous but fleeting. The car in front of us entered and disappeared.

We drove into the dust cloud. We could no longer see the road, let alone the front of our car. I expected it to end as soon as it started but the seconds started to pass. Each second felt like a minute — our safety hanging in limbo. The number of thoughts you can have in a matter of seconds is remarkable. Am I still in my lane? I think I would hit bumps on the road if I veered too far off. Should I slow down? The car behind us might hit us. Did the person in front of us slow down? The seconds drew on, long enough to voice these questions out loud.

In the end, I did nothing. I kept driving and held the wheel straight. After several coursing seconds, we made it out. It was one of the closest calls I've had driving and yet no adrenaline spiked and no quick maneuvers were made. Only the realization after, just how dangerous that had been.

March 11, 2021

NFTs, Non-Fungible Tokens, have broken into the mainstream in the past few weeks.

Nifties, as some call them, are a way to prove ownership of a digital asset like a photo, illustration, or video using the blockchain. NFTs aren't a currency like BitCoin or Ethereum, they have no intrinsic value, the value of an NFT comes from the value of the actual asset that it represents.

The NFT markets have been skyrocketing and this excitement and energy is largely centered on NFT art. Digital art is being sold as NFTs. It usually comes in the form of a jpeg, gif, mp4, or other common image and video file formats. NFTs contain metadata and the metadata usually includes a link to the artwork, hosted elsewhere on the web. This means the artwork itself is not on the blockchain but the certificate of ownership, NFT, is on the blockchain or public ledger.

Because of this, if you want to use one of these pieces of art as your background image on your device, there's nothing in place to stop you. The file is public and anyone can right-click and download it just like any other image on the internet.

So then why own NFT art?

Nifty art has little utility beyond being able to display it in somewhat obscure virtual worlds. Some say to buy them for bragging rights, because owning things feels good, or to start a collection. Maybe that resonates with you, maybe, like me, it doesn't. I'd say a lot of people are buying NFT art because they don't understand the speculation trap they've fallen into.

NFTs today, feel a lot like the crypto craze of 2018. The difference is that NFTs have no inherent value, are not a store of value, and there's no way to get your money out besides finding a buyer for your specific token.

The nifty art market is based on artificial scarcity, hype, and speculation. Artificial scarcity isn't enough to drive lasting value. Almost every article about NFTs mentions Crypto Kitties as an example of the power of artificial scarcity but they overlook the fact that the cats offered utility in that you could breed them and sell the offspring to others, for a profit.

Beyond art

While nifty art is getting all the attention, there are very practical use cases for NFTs that will outlast the art craze.

NFTs have two important traits that make them valuable tools. They are tradable on open markets and they prove ownership. Taking advantage of these traits to provide utility is where the real value of NFTs comes from. NFTs are not art. Art is just one type of asset that an NFT can represent and it's important to remember that.

These are a few of the real-world scenarios where NFTs could be game-changing.

Tickets

Tickets are a great use case for NFTs. Tickets to concerts, games, conferences, or other events and shows — both in person and online. In this case, there are already markets set up to sell your tickets to another person but they're fraught with uncertainty and fraud. Oftentimes, all you need is a barcode printed out or on your phone to get into an event. If you bought the ticket second-hand, which has become increasingly the only way due to a proliferation of bots buying tickets as soon as they go on sale, how can you be certain that they didn't sell the same barcode to someone else or several others? First one to arrive gets in and everyone else got scammed. That's not a good system.

There have been attempts to make physical tickets have holographic or otherwise hard to counterfeit traits but to a one time buyer I wouldn't know how to know if what I'm looking at is real or an elaborate fake.

This is what provability of ownership solves. With the NFT, you can see that the ticket originated from the official organization in charge of the event and there is no way for someone else to use the ticket. You can be sure that you didn't buy a fraudulent ticket and be sure that it will work to get in.

Movies

Movie ownership feels a little different than it used to. We used to own DVDs. They were ours and we could do what we wanted with them, permitting we weren't breaking laws. These days, if you click buy in iTunes, what do you get? You get the privilege to watch the movie from your account, in iTunes, and that's it. There's no transferability. You can't move that movie to Google Play or lend it to a friend because you didn't buy the movie. You bought a privilege to consume it through a service.

If digital movies instead used the NFT model, you would own a copy of the actual files and you could watch that movie through any service or player that you want. You could give it to a friend for the weekend. When you're tired of it, just like a good old-fashioned garage sale, you could sell it off.

I don't foresee large movie studios being at the forefront of this innovation, they are currently benefiting from this lopsided system. I would expect smaller studios and independents to be the driving force in this change.

Game assets

Have you ever bought in-game items? If you have, you've given money to the makers of the game, and in return, you get to use the item with your account in their game and that's it. If instead, you bought an NFT that represents a digital asset in the game, doors start to open. You could exchange your items on a public marketplace. You could show off the items you own on your blog. If you no longer play this game, you can sell off all of your items.

At first, it feels like all of the power is being given back to the users and the game makers are losing if they adopt NFTs. But, they won't lose if they adapt. NFTs create more reason to buy items and an ability for users to show off items which drives demand for more limited-run items. The switch to an NFT model creates a more thriving market and the ability for the game makers to produce more items to sell.




There are so many more ways that NFTs can and are being used but just because they can be used, doesn't mean that you should. No article about crypto would be complete without including a mention of the environmental effects of using crypto, which is enormous. With such high environmental costs, NFTs should only be used when it offers real utility and value that is hard to obtain otherwise.

To understand the environmental costs, I'll send you to this article explaining them. https://everestpipkin.medium.com/but-the-environmental-issues-with-cryptoart-1128ef72e6a3

February 25, 2021

I've become mildly obsessed with box-shadows. When they are done well, they're magic. They provide depth, contrast, and clarity to the page. When done poorly, they're distracting. It's apparent to me that most shadows fall short of the magic. It's especially true when I try to apply my own handwritten CSS box-shadow to an element.

What's behind a good box-shadow?

I keep finding myself reaching for the shadows defined in Material Design. They are some of the only ones that I think really get it right. Most design frameworks provide their own version of a decent shadow but I find the concept and execution of elevations in Material Design resonates with me. They also have extensive design guidelines that are worth reading.

I never understood why Material Design shadows are so good and when I make a shadow, it really doesn't look great. They come out too harsh or undefined and unprofessional.

What's the math behind box-shadows?

I see a lot of visual editors like WebFlow or box-shadow generators that give you controls for angle and distance, but angle and distance aren't CSS properties for box-shadow. So then, how do you get the x and y values from angle and distance? Hint: trigonometry is involved.

Why don't CSS generators produce good-looking shadows?

The quick answer is that most of them don't support layers and as you're about to learn, having multiple shadow layers is important.

I had these questions and one night my curiosity got the better of me. At least, I had hoped it would only be one night. It led me back to high school trigonometry and carried me through a weeks long journey into box-shadows. This journey involved building a CSS Box-Shadow generator myself and writing this article that keeps growing in scope.

Alas, here we are, let's get into it.

CSS Box-Shadow

Let's take a look at what the box-shadow CSS property is and the values it allows.

Syntax: box-shadow: x-offset y-offset blur-radius spread color outset/inset

Example: box-shadow: 0px 3px 2px 4px #000;

Box-Shadow Values Explained

x-offset — This is the horizontal offset of the shadow. Higher values will move the shadow to the right like it is traversing the x-axis of a graph. Negative values will move the shadow to the left.

y-offset — This is the vertical offset of the shadow. Higher values will move the shadow further below your object or down the page. Negative values will move the shadow higher vertically up the page. This is the opposite direction you might think of when looking at a graph with the y-axis.

blur-radius — Higher values will increase the blurriness and also increase the size of the shadow leading the shadow to become lighter in color. This number can't be less than 0.

spread — Higher values will increase the size of the shadow without causing any loss of sharpness.

color — Specifies the color of the shadow.

* outset / inset — You can specify the box-shadow to be inset instead of the default outset. This will make the shadow appear inside of the object instead of outside of it. This article is about outset shadows and won't touch on inset shadows.

There's a little bit more to the box-shadow that I did not cover here and I encourage you to look it up elsewhere if you need to know more. This is a good basic understanding for us to use.

The thing that isn't obvious while working with box-shadows is that you can specify multiple shadows on the same element. Simply, use commas to separate multiple values.

Angle and Distance

It's very common to see the inputs angle and distance in visual editors such as WebFlow and they forgo the x and y offsets. You'll notice though that neither angle nor distance is one of the attributes defined above.

How do angle and distance translate to x-offset and y-offset?

To answer this, we need to dive into some trigonometry.

The x offset and y offset can be thought of as the x and y coordinates on a graph. Because of this, we can draw a right triangle on the graph from the center point. If you can remember back to your trigonometry days, right triangles are the very basis of the 6 fundamental trig functions.

I'm going to show you how to calculate the values forwards and reverse. From angle and distance to x and y and then from x and y to angle and distance.

Math

Take a look at this graph. We're going to use it as the basis for our calculations.

Trig Chart

The x and y coordinates are the x-offset and y-offset of the box-shadow properties. When you plot it onto a graph, like the graph shows, it creates a right triangle and the perfect setup for doing a little trigonometry.

If you understand trig or stare at this graph long enough you'll see how all the pieces fit together. When given the options of angle and distance instead of the x and y, we can see that the angle is angle A in the graph and distance is c or the hypotenuse of the triangle formed.

These are the two basic functions of trig and the two that we need for our calculations.

sin(x) = opposite/hypotenuse = a/c

cos(x) = adjacent/hypotenuse = b/c

Angle and distance to x and y

Angle and distance to x and y is the calculation that visual editors would use. We let the user pick an angle and distance and then we convert that into an x and y coordinate to use as the x-offset and y-offset in CSS.

We can use the functions defined above.

X in our case is the angle, we'll call it angle A as we can see on the graph.

a is our y offset.

b is our x offset.

c is our distance.

In this case, we know the values of angle A and the distance, c. So we need to solve for a and b.

We will need to use both the sin and the cos functions. Sine will give us a, our y, and Cosine will give us b, our x.

Chart

If we rearrange the functions they will look like so.

cos(A) * c = b

cos(A) * distance = y

sin(A) * c = b

sin(A) * distance = x

Using real numbers

angle = 70

distance = 9

sin(70) * 9 = x

3.08= round down to 3 = x

cos(70) * 9 = y

8.45= round to 8 = y

(x, y) = (3, 8)

x and y to angle and distance

This is the calculation to take the x-offset and y-offset from our CSS values and convert it to an angle and distance that we could show to a user in a visual editor.

a is our y offset.

b is our x offset.

In this case, we know the a and b values and we need to solve for angle A and distance.

Distance

We know that distance is c and c can be calculated using good old Pathagoreum's theorem. a^2 + b^2 = c^2.

distance = c = sqrt(a^2 + b^2)

Angle

To find the angle, we need to do the inverse of the previous calculations, we need to use inverse sine, arcsin, or inverse cosine, arccos. We can use either arcsin or arccosine because we know the values of each of the sides of the triangle.

We can take the inverse of our function from before and solve for the A in sin(A).

A = arcsin(a/c) or in our terms Angle = arcsin(x/distance).

Using real numbers

We'll use the values we calculated from our angle and distance.

x = 3

y = 8

End result should give use angle = 70 and distance = 9 because those were the values that we used above.

distance = sqrt(8^2 + 3^2) = sqrt(64 + 9) = sqrt(73) = 8.54 = round to 9

angle = arcsin(8/sqrt(73)) = 69.44 = round up to 70

That's it, that's how it's calculated. Not too bad if you think about it for a minute.

These calculations need to be done in radians. If none of the math worked for you, it's likely because your calculator is in degrees mode and you need to flip it to radians.

I built this quick calculator tool that you can use to test your math or inspect to see how it might be done in javascript.

What makes a box-shadow great

Color and opacity, layers, and angle and distance are the three critical parts needed to make a great natural shadow. Get all three right there's no shadow you won't be able to conquer.

Color & Opacity

When creating a shadow by hand, the shadow often appears too dark. A couple of ways that you might be tempted to mitigate this is to change the color to something less black and more gray, off-white, etc. The other attribute you might reach for is the blur. Crank up the blur and it will make the shadow appear less dark and sharp due to the lack of concentration the blur provides.

These strategies might get the job done but it's sloppy and won't give you the best result possible.

A great shadow mimics our perceived physical reality. So then, a contemplative person might ask — what is a shadow? A shadow is caused by an object blocking light and causing a partial absence of light on the surface behind or below the object.

With that as our working definition of a shadow, it changes the way we might think about our options for making a shadow less harsh and more natural.

Let's think through it again.

Black is the absence of light.

A shadow is the partial absence of light.

Therefore, a shadow in our perceived reality is a semi-transparent black.

In CSS, that would mean setting the opacity of our black to less than one. Setting the color to a semi-transparent black now frees us to use the blur value to shape our shadow and not use it to compensate for something else.

Semi-transparent black will also allow your shadow to keep looking good on multiple background colors because the transparency will allow the background color to continue to shine through.

Recommended Opacity Range: 0.04 - 0.25

eg. rgba(0,0,0, 0.2)

Layers

It's not overtly obvious that box-shadows supports multiple shadows on one element but this is the key to a great shadow.

To understand why layering works so well, we need to look at a film technique.

3 Point Lighting

There is a well-established technique in film and photography used to light subjects called 3 point lighting. It solves a simple problem. You need to light a subject, often a person, using artificial light sources but in a way that it looks pleasing. Using a single bright light will cast harsh shadows. Three-point lighting is a technique that solves this problem with a relatively low number of lights, three.

In a traditional setup, you have three types of lights, key light, fill light, and back light. The key light is your brightest source and is often pointed from off-center to the front of your subject. The fill light is a softer light that is often pointed from the other side of the front of your subject to fill in the shadows caused by the key light. The back light is a light that is pointed at your subject from behind and acts to give your subject some definition around the edges.

It's important to realize that a single artificial light source is often too harsh whether you're talking about photography or CSS.

Apply this to CSS

To layer shadows, they must be semi-transparent. If they are not, they cannot compound on each other and will rather block one another making the previous layer mostly useless.

You can think of each layer as being one of the three lights. One of your shadow layers will act as your key light, it will be the heaviest shadow — least transparent. A second layer can be your fill light, a little softer and wider. Lastly, your back light, provides a little extra definition to your edges.

A Material Example

Take a look at how Material Design uses layers to define their shadows.

elevation-3-break-out

To see the effect of the layering and what impact each has, I've turned the shadows into red, green, and blue. Opacity 20 layer is red. Opacity 14 layer is green. Opacity 12 layer is blue.

Notice how the colors interact with each other and create colors, like purple, that weren't used. This also highlights the effect that each shadow has on the box.

material-z3-in-rbg

Elevation

The way a shadow is cast can signify how high above the surface the object is floating. Using different 'elevations' of objects can be an important indicator in your design.

The further an object is from a surface, the larger the shadow it will cast. In CSS this would translate to a larger blur-radius and spread.

The higher an object is, the further down the screen you'll want the shadow to appear.

Using a combo of blur-radius, spread, and y-offset will allow you to portray height.

Into the shadows

With that, my journey comes to an end but hopefully, this is just the beginning of your journey. To experiment with box-shadows, I encourage you to try out the box-shadow generator tool that I built. Good luck. Hope to see you in the shadows.

Get new posts to your inbox
I'll send you an email when I publish a new post