Hi Fellow Newbies!
Click here skip "housekeeping notes" and jump directly to today's tutorial.
Two quick housekeeping notes before today's write-up:
First: Moving forward with this series, I'm going to start posting future entries exclusively here on CodeNewbie. A week ago, I originally first posted on dev.to (and then ultimately cross-posted here on CN) but at the time I honestly didn't understand that CN was actually its own Forem community. (Like, I'd originally thought CN was just a hashtag on dev.to.)
Anyway, after posting, for days afterwards I often continue to edit and tweak my entries. (OCDs gotta OCD!) But cross-posting is a huge pain because you need to make the same edits in multiple places. So I'm thinking I'll just write up this entire beginner's series on CodeNewbie first and then once I've finished the entire series, maybe then cross-post. (Though it's worth noting that dev.to does currently have a much larger userbase.) And I'm currently also contemplating some other posts on other more advanced topics which I think I'll post on dev.to. (Honestly, at the moment, I think I'm not uniquely confused.) But to me at least, what seems sensible is that "newbie" stuff goes on CodeNewbie. And more intermediate stuff goes on dev.to. Right? I think? 🤷
Second: I've been trying to think about how to make these tutorials more "newbie-friendly." To this end: I'm just going to make the guide a series in "How to…" without any of the technical jargon in the titles anymore (eg. Words like "authorization" and "authentication".) Being a newbie nowadays means, more than anything else IMHO, knowing the "right" questions to ask on Google. But there's a genuine epistemological challenge here, if you think about it. Like, if you don't know what "Object-relational mapping" (ORM) is, then you're not going to need to know you need to actually search for it. (Even if it's what you really need to learn and know!)
Thus, moving forward, I'm just going to start writing a series about "Building a Blogging System From Scratch." One, it's a nice toy-project I do think is instructive about the basics. Viz. Total newbs can see that title and actually understand it. Two, selfishly, I'm using this guide writeup as a vehicle to personally learn Firebase (Firestore currently, but eventually Google Cloud Functions and the rest of it). Three, there are genuinely some ideas I have for a "better editor" that I honestly currently don't see anywhere else. (To be fair, I'm sure these ideas are not unique and someone's already coded it. But nothing wrong with reinventing the wheel! ✊)
Alright, all that housekeeping out of the way, let's jump in to today's lab! 😀
Today's Goals:
"Data Modeling" - Object-Relational Mappings (ORMs)
How to get data back from the database (Firestore) and how to work with it?What are your "User Stories"?
Who uses your app? What are their different personas and what paths will they take? What does your app actually look like in each path?"Fatten" those models!
Adding methods and "calculated fields" to your models.
1. Data Modeling
At some point in every software project, you need to present data to your users. To this end, a phrase you'll hear often is "mental model". What is the "mental model" of the space or situation you're trying to represent? One common example: Users
. All apps have users, right? Well, that's your first model right there! Common attributes of a User
model: Email, name, etc.
For our humble blogging app, we've got two models set up so far. (A quick heuristic, btw: Often, each table/collection in your database will be its own individual model. Not always, but often.) So for us, for example, we've got an authorized
collection of our users:
But how does data from the database get into your actual code? Ie. In our case, JavaScript?
Enter Object-Relational Mapping tools.
Virtually every single modern framework nowadays will feature an ORM tool that connects the data in your database to your programming code. (Eg. Hibernate for Java, Entity Framework for .NET, ActiveRecord for Ruby on Rails, etc.) And Firestore is no different.
Firestore's ORM is pretty rudimentary so you'll need to manually write the boiler (ie. "boilerplate") yourself (some other ORM tools in other languages will auto-generate this code for you) but it basically looks like this:
/public/lab02/models.ts
:
export class User {
fullname: string;
constructor (
public id: string,
public email: string,
public firstname: string,
public lastname: string,
public logins: [Date],
public role: string
) {
this.fullname = `${firstname} ${lastname}`
}
}
export const userConverter = {
toFirestore: function (user: User) {
return {
email: user.email,
firstname: user.firstname,
lastname: user.lastname,
role: user.role
}
},
fromFirestore: function (snapshot: any, options: any) {
const data = snapshot.data(options)
const id = snapshot.id
return new User(id, data.email, data.firstname, data.lastname,
data.logins, data.role)
}
}
Source:
/public/lab02/models.ts
As you can see, every field in our authorized
Firestore collection (our database) corresponds to a property in our JavaScript User
class.
2. User Stories / "Coders are Creatives"
Continuing with our User
example, let's now take it a step farther. Here's where it becomes helpful to think about the actual "User Story" that you'll be expecting when users use your app. For example, what are the different modalities, use cases, paths? (Ie. "User Journeys", "Storyboards", "User Stories", etc?)
Up until now, we've had a general gist of "creating a blogging app" as our goal. But now it's time to get more specific. Meaning: What does our blogging app specifically look like? What is the operational flow? What does a user actually see the moment they log in? A dashboard with fancy tables and graphs? A list of all published articles? A list of only the user's published articles? A giant .png of the Octocat mascot?
The act of coding is an act of creation. Historically, I know there's some misguided meme about coders/engineers being kept in the dark/dank basement and being fed and given instructions via pneumatic tube and never seen, but that understanding is seriously so outdated. That was the working model back when computers were the size of houses and it took a triple-PhD in rocket-surgery to operate them.
But nowadays, coders are creatives. It's the general public. It's mom and pop, people of all ages and backgrounds who are just randomly plucked off the street. It's no longer some provincial domain for the rarified and esoteric air of experts and professionals. Coding is for everyone. Capitalism and corporations' insatiable drive for greed and evermore money/eyeballs/mindshare (Microsoft owns GitHub!) has democratized technology for all. You're welcome.
So if you want to build an app, before you write a ton of code, you'll need to think about what your app actually looks like. To this end, I often work backwards. I devise quick screen mocks of the different flows of what I want my app to look like visually from the user experience. This can often be done in just raw HTML but you can also use PowerPoint or Photoshop or MS Paint. Or if you're really gifted and are working solo (and don't need to share with anyone for feedback/signoff/etc), you can just even brainstorm it in your own head. The important part is just that you have a more specific idea of what you wish to build. You need to know what distant lands you wish to conquer before you can actually march your army there, right?
Thus, for our blogging example today, here are some quick mockups (end results). I have decided: As soon as a user logs in, if they are authorized, they should see a list of the three most recently published articles.
Thus:
- If John McClane tries to sign in, he just gets booted:
- Bruce Wayne is a
reader
-- if he signs in, he's able to read all articles.
- Diana Prince is a
writer
-- if she signs in, she's able to read all articles, as well as edit/delete her own.
- Clark Kent is an
admin
-- if he signs in, he's able to read/edit/delete all articles.
Okay, so far so good, we're slowly getting there! We've got our mocks of what we want the sausage to look like. Now we just need to actually make it. Let me take a quick moment here and highlight some important bits:
3. "Fat Models": Methods and "Calculated Fields"
Next, let's take a quick detour into the land of "calculated fields". Recall our User
model. We've got all of the standard, expected fields like firstname
, last name
, email
etc. But the canonical example is often on the GUI (like in the byline), we want to display the user's full name. And so for convenience, it's a common practice to add attributes that you'll access frequently from the GUI to your model. That way you can call them directly. In our user model, for instance, notice that I've added a fullname
property that is calculated from ${firstname} ${lastname}
.
Our example is very trivial here but you can imagine in other apps where these kind of "calculated fields" can prove extraordinarily useful. A general good rule of thumb is that calculated fields are often fields you don't want to store in your database but you want to have easy access to them on the GUI.
This actually segues nicely to our fourth and final section for today:
4. The most valuable coding advice I ever received.
On blog entries, it's common to associate articles to tags. Let's say Wonder Woman is concerned about #jlasowhite (I know Khal Drogo and the Cyborg guy aren't white but let's just roll with this example for the moment, please) and writes a blog post that's modeled like this:
So let's muse about this for a moment. I've got an array of strings that I wish to turn into hash tags. Eventually, the idea is that any reader will be able to click on any of those hash tags and bring up a list of all articles related to those subjects. Pretty standard stuff. But implementation-wise, how do we build this?
Off the cuff, I think two solves will intuitively come to mind: One not good way and one better way. In the spirit of showing less stellar approaches (we were all newbies once! We've all been there!), let's walk through a perhaps intuitive but less-than-stellar approach:
First approach: We could put it in our article
model. Similar to how we created a calculated field in our user
model for fullname
, you could likewise add a tagStr
calculated field to your Article
model. It'd look like this:
/public/lab02/models.ts
:
export class Article {
tagsStr: string;
constructor (
public id: string,
public author: string,
public content: string,
public datetime: any, // firebase.firestore.Timestamp
public email: string,
public tags: [string],
public title: string,
public uid: string
) {
this.tagsStr = tags.reduce((acc, tag) => {
acc += `#${tag}, `
return acc
}, '')
}
}
…
Source:
/public/lab02/models.ts
which yields:
Okay, tags are now displayed on the GUI. But they need to be clickable links, right? So let's go a step farther and also say you wish to nicely style each of those individual tags as hyperlinks and make them color-coded and beautiful. Being the intrepid and clever coder you are, you may be tempted to do something like this:
/public/lab02/models.ts
:
…
this.tagsStr = tags.reduce((acc, tag) => {
acc += `<a href="javascript:getArticlesByTag('${tag}');"
style="background:yellow;">#${tag}</a>, `
return acc
}, '')
…
Source:
/public/lab02/models.ts
which yields:
So that definitely technically works and you've achieved your desired end result of what you wished to render. But recall our previous lab: There are often many ways to do things. And while there may be ultimately multiple right answers depending on the situation, there are also most definitely many NOT GREAT answers too. (And spoiler-- this is definitely not a great way. Though it works!) The example I always like to give is a basketball analogy. We can debate and argue for hours whether MJ or Lebron is the GOAT. But surely, we can also agree that the GOAT is most definitely not [put name of some obscure basketball journeyman here], right? Just because there are multiple "right" answers/opinions doesn't mean all answers/opinions are good!
This post is getting kind of long so I'm going to break it off here… ("we'll leave it as an exercise to the reader as to brainstorm better ways to implement tags..."; don't worry, in the next lab, we'll cover a better way to do it.) but before I sign off today, I want to give a side-rant on what I think is the most important coding advice I've ever received:
As this above --trivial perhaps, but still instructive, I submit-- example illustrates:
In a fantasy/perfect world, you'll know exactly what you're building the first time you build it.
(Eg. Here, we know we want a "tagging system" for our articles. But where does the logic and formatting go? In the model? In the GUI widget? Elsewhere? 🤷)
But as ridiculous as it sounds, most of the time, as a coder, you will actually not know exactly what you're building. The truth is far more organic. For many projects, you'll have a general idea of what you want to build. And then you just kinda start. And in the course of solving one big challenge (ie. "Build a blogging system"), you'll solve hundreds (thousands!) of small problems (ie. "How to do I authenticate my users? What's the authorization scheme? What do I model my data? How do I center this CSS div to make my GUI look cool?).
So one of the best pieces I ever received about coding is to NOT overthink things.
JUST START.
(And then, just keep charging forward.)
Especially when you're first getting started, you're going to often get to design questions where you're unsure what the answer should be. To a large part: It's because you don't even know what your users will want! (This is what A/B testing, user studies, Google Analytics, is all for. Later.) So, my humble advice: Yes, try your best to be thoughtful but don't get hung up either. Just make a choice and move on. Code is honestly very cheap to write and very replaceable. * Simply resign yourself that the first time you make your app, you're just "gonna build it wrong." But that's what rewrites are for! Even the best of us (I invite you to go Google the tale of AngularJS/Angular and its evolution) don't always get it right. Simply because we don't fully yet know what we want or what our users (ie. the marketplace) will want. And that's fine. It's just part and parcel of coding. Coding is a lot like writing: At least half of it is in the rewrite.
Just get it down on paper (on screen) and move on to the next thing.
Resign yourself to the rewrite.
* Really, the only exception I can think of, where building and iterating quickly is bad, is if you're coding for something like SpaceX where you're trying to launch a rocket to Mars and human lives are on the line. Like, I've never worked at NASA or JPL but I imagine in those environments where you can't just "move fast and break things", tons of care and effort up front are poured into writing highly specific and technical documents detailing in fine grain every single decision, for-loop, etc before a single line of code is written. Just because the stakes are so high. Bajillions of dollars on the line. Human lives at risk. So on, so forth. But for the rest of us mere mortals not in Pasadena, I'm telling you: It's honestly not like that. We literally just start and then continue building and tinkering with the plane while it's midair, in flight.
Alright, and that's it for this weekend! I really only get a chance to write these things up during the weekend. So sorry if progress is a little slow. Tune in next weekend though! We'll finish up this tagging question (a better way awaits on the horizon!) as well as dive into some GUI stuff. I'm super-excited that we're getting increasingly closer to "the editor"-- what I think it going to be "the fun part". See you guys next time! 😀 👋
Top comments (1)
Building a blogging system from scratch involves catering to different personas such as content creators, editors, and readers. Content creators need an intuitive interface for writing and managing posts, along with tools to enhance their content like Lightroom for photo edits, while editors require robust workflows for reviewing and approving posts. Readers benefit from an organized layout with easy navigation and search functionality. Additionally, integrating features that differentiate between Lightroom vs Photoshop capabilities can enhance user experience by allowing seamless photo editing directly within the blogging platform for detailed image manipulation or batch edits. This approach ensures a comprehensive system that meets the diverse needs of all users.