The Front-End Fresh Workshop


Agenda
Sept 25th
Front-End Engineering in 2024 & Mastering React

Oct 2nd
Building Large Scale Web Apps
Oct 9th
Building a Personal Brand & Breaking Into FAANG
Oct 16th
Leveling Up From Junior To Senior To Staff

Today's Agenda
Building Large Scale Web Apps

- State Management
- Personalization & A/B Testing
- Deployment + Monitoring
- Design Systems & Component Libraries

Introductions!
Adeyemi
Maliha
today's folks
Asmaa
Emad
Satya
Richard







Q/A!
Any questions before we get started?

CSS
<form>
<label for="name">Name:</label>
<input type="text" id="name" name="name" />
<label for="email">Email:</label>
<input type="email" id="email" name="email" />
<input type="submit" value="Submit" />
</form>HTML
form {
max-width: 400px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
background: #f9f9f9;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
/* ... */CSS

React
import React from 'react';
import './ContactForm.css';
function ContactForm() {
return (
<form>
<label htmlFor="name">Name:</label>
<input type="text" id="name" name="name" />
<label htmlFor="email">Email:</label>
<input type="email" id="email" name="email" />
<input type="submit" value="Submit" />
</form>
);
}
export default ContactForm;
form {
/* ...styles from your initial CSS... */
}
label {
/* ...styles from your initial CSS... */
}
/* Additional styles if needed */
./ContactForm.css

React
import React from "react";
import styles from "./ContactForm.module.css";
function ContactForm() {
return (
<form className={styles.form}>
<label htmlFor="name" className={styles.label}>
Name:
</label>
<input
type="text"
id="name"
name="name"
className={styles.input}
/>
<label htmlFor="email" className={styles.label}>
Email:
</label>
<input
type="email"
id="email"
name="email"
className={styles.input}
/>
<input
type="submit"
value="Submit"
className={styles.button}
/>
</form>
);
}
export default ContactForm;.form {
/* ...styles from your initial CSS... */
}
.label {
/* ...styles from your initial CSS... */
}
.input {
/* Input-specific styles */
}
.button {
/* Button-specific styles */
}
CSS Modules
./ContactForm.module.css

CSS Frameworks


CSS Frameworks


CSS Frameworks


CSS Frameworks


CSS Frameworks
import React from 'react';
const Button = ({ label, onClick, variant = 'primary' }) => {
const baseStyles = 'py-2 px-4 font-semibold rounded';
const variantStyles = {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-500 text-white hover:bg-gray-600',
danger: 'bg-red-500 text-white hover:bg-red-600',
};
return (
<button
className={`${baseStyles} ${variantStyles[variant]}`}
onClick={onClick}
>
{label}
</button>
);
};
export default Button;

CSS Frameworks
import React from 'react';
import Button from './Button';
function App() {
return (
<div className="flex justify-center items-center h-screen">
<Button label="Primary Button" onClick={() => alert('Clicked!')} />
<Button
label="Secondary Button"
onClick={() => alert('Secondary clicked!')}
variant="secondary"
/>
<Button
label="Danger Button"
onClick={() => alert('Danger clicked!')}
variant="danger"
/>
</div>
);
}
export default App;





Component Libraries


Component Libraries


Component Libraries
import * as React from "react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
export function CardWithForm() {
return (
<Card>
<CardHeader>
<CardTitle>Create project</CardTitle>
<CardDescription>Deploy your new project in one-click.</CardDescription>
</CardHeader>
<CardContent>
<form>
<div className="grid w-full items-center gap-4">
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Name of your project" />
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="framework">Framework</Label>
// ...
</div>
</div>
</form>
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="outline">Cancel</Button>
<Button>Deploy</Button>
</CardFooter>
</Card>
)
}
Component Libraries
and so many more...

Component Libraries
What do companies like Doordash, Shopify, Meta, etc. do for their React apps?

Component Libraries
They often develop their own comprehensive component libraries and design systems for their React apps to ensure consistent UI/UX, improve developer efficiency, and maintain scalability across their platforms.
They develop their own to to address unique business needs and enforce brand consistency.

Component Libraries
and a lot more...

Component Libraries


Component Libraries


Design Systems
Component libraries are just one aspect of an overall Design System
A Design System is a collection of reusable components, guidelines, and assets that help teams build cohesive products.

Design Systems
Component libraries
Design Tokens
Typography Guidelines
Branding
Documentation
Iconography
Collaboration Tools (e.g. Figma)
etc.

Design Systems
Front-End Engineers
Designers
Product

Design Systems
Mobile (iOS)
Mobile (Android)
Web

Design Systems
Q/A!
and a small 5 min break...

State Management


State Management


State Management


State Management


State Management
import React, {
useState,
createContext,
useContext,
} from "react";
// Create a context
const MessageContext = createContext();
function App() {
const [message, setMessage] = useState(
"Hello World!",
);
return (
// Provide the state to nested components
<MessageContext.Provider
value={{ message, setMessage }}
>
<Child1>
<Child2>
<Child3>
<DeeplyNestedChild />
</Child3>
</Child2>
</Child1>
</MessageContext.Provider>
);
}
State Management
function DeeplyNestedChild() {
/*
Use the data directly without receiving it
as a prop
*/
const { message, setMessage } = useContext(
MessageContext,
);
return (
<div>
<h1>{message}</h1>
<button
onClick={() =>
setMessage(
"Hello from nested component!",
)
}
>
Change Message
</button>
</div>
);
}
State Management
import React, { useState } from "react";
function App() {
const [message, setMessage] = useState("Hello World!");
return (
<div>
<h1>{message}</h1>
<button
onClick={() => setMessage("New Message!")}
>
Change Message
</button>
</div>
);
}
export default App;

State Management
import React, { useReducer } from "react";
const initialState = {
message: "Hello World!",
};
function reducer(state, action) {
switch (action.type) {
case "CHANGE_MESSAGE":
return {
...state,
message: action.payload,
};
default:
throw new Error();
}
}
function App() {
const [state, dispatch] = useReducer(
reducer,
initialState,
);
return (
<div>
<h1>{state.message}</h1>
<button
onClick={() =>
dispatch({
type: "CHANGE_MESSAGE",
payload: "New Message!",
})
}
>
Change Message
</button>
</div>
);
}
State Management
The additional steps in useReducer are helpful for separating the logic of how state is updated from the UI components.
This makes the management of data flow clearer and more predictable. With useReducer, state updates have descriptive action types, which help make it easier to trace where and how state changes occur.

State Management


State Management
import {
createSlice,
configureStore,
} from "@reduxjs/toolkit";
const initialState = {
message: "Hello World!",
};
export const messageSlice = createSlice({
name: "message",
initialState,
reducers: {
changeMessage: (state, action) => {
state.message = action.payload;
},
},
});
export const store = configureStore({
reducer: messageSlice.reducer,
});
State Management
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { App } from "./App";
import { store } from "./store";
const rootElement =
document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<Provider store={store}>
<App />
</Provider>
);
State Management
import React from "react";
import {
useSelector,
useDispatch,
} from "react-redux";
import { messageSlice } from "./store";
export function App() {
const message = useSelector(
(state) => state.message,
);
const dispatch = useDispatch();
const handleChangeMessage = () => {
dispatch(
messageSlice.actions.changeMessage(
"New Redux Message!",
),
);
};
return (
<div>
<h1>{message}</h1>
<button onClick={handleChangeMessage}>
Change Message
</button>
</div>
);
}
export default App;
State Management


State Management


State Management
- First, start with data-fetching considerations
- Next, gauge the necessity for more robust custom state management solutions.
- Evaluate the merit of simpler state management tools.
- Finally, always keep component state at the component level

State Management
Q/A!

Personalization & A/B Testing
Personalization refers to the practice of creating tailored experiences for individual users or groups based on their preferences, behavior, or other identifiable attributes.

Personalization

A website personalization example from Amazon; product recommendations based on customer data and intent

Personalization
A website personalization example from Netflix; viewing recommendations based on customer data and reviews


Personalization
import React from "react";
// Create UserPreferences context
const UserContext = React.createContext({});
export const UserProvider = ({ children }) => {
/*
get user preferences information from
external service/API
*/
const userPreferences = getUserPreferences();
return (
<UserContext.Provider
value={{ userPreferences }}
>
{children}
</UserContext.Provider>
);
};Using context to store user preference info

Personalization
import { UserProvider } from './UserContext';
function App() {
return (
<UserProvider>
{/* other components */}
</UserProvider>
);
}
export default App;
A/B Testing
A/B testing, also known as split testing or controlled experiments, is the method of comparing two or more versions of a web page, feature, or product against each other to determine which one performs better.

A/B Testing
Segmenting users
Serving different versions
Collecting data
Analyzing experiment results

A/B Testing




A/B Testing
Button Color Test
Hypothesis: Changing the color of a certain button to green will significantly increase the click-through rate when compared to the existing button color blue.
Objective: Determine which button color leads to higher click-through rates.
Setup: Create two variations of a button, one blue and one green. Serve one variation to 50% of all users and the other variation to the remaining 50% of users.
Data collection: Track button clicks for both variations using Statsig’s built-in event tracking.
Analysis: Compare the click-through rate of both variations to determine which color is more effective.

A/B Testing


A/B Testing


A/B Testing
import { StatsigProvider } from "statsig-react";
function App() {
return (
<StatsigProvider
sdkKey="<STATSIG_CLIENT_SDK_KEY>"
waitForInitialization={true}
>
<div className="App">
{/* Rest of App ... */}
</div>
</StatsigProvider>
);
}
export default App
A/B Testing
import { useExperiment } from "statsig-react";
function ButtonComponent() {
// access experiment configuration
const { config } = useExperiment(
"button_color",
);
// access value of experiment parameter
const showGreenButton = config.get(
"enable_feature",
);
return (
// ...
)
}
export default ButtonComponent
A/B Testing
import {
Statsig,
useExperiment,
} from "statsig-react";
function ButtonComponent() {
const { config } = useExperiment(
"button_color",
);
const showGreenButton = config.get(
"enable_feature",
);
const buttonColor = showGreenButton
? "green"
: "blue";
const onButtonClick = () => {
// log button click
Statsig.logEvent(
"button_click",
buttonColor,
);
};
return (
<button
style={{ backgroundColor: buttonColor }}
onClick={onButtonClick}
>
Click Me
</button>
);
}

A/B Testing


Feature Flags
In a large-scale web app, when we launch a new feature to users — there can be many instances where we may not want to launch the feature to 100% of all users right out of the box.

Feature Flags
Mitigate risk
Collect user feedback
Test the market
Ensuring a rollback plan

Feature Flags


Feature Flags


Feature Flags
import { useGate } from "statsig-react";
function ButtonComponent() {
// Evaluate the feature flag
const { value: isFeatureEnabled } = useGate(
"new_button_color",
);
// Decide which button color to use
const buttonColor = isFeatureEnabled
? "green"
: "blue";
return (
<button
style={{ backgroundColor: buttonColor }}
>
Click Me
</button>
);
}
export default ButtonComponent;
Feature Flags
Q/A!

Deployment & Monitoring
Deployment — getting our code from development to production (i.e., to our users).

Deployment

npm run build

Deployment

Static site

Deployment





Deployment
Client (React) app
Server app
Database
AWS/Digital Ocean/Heroku/etc.
Host Postgres/MongoDB

Deployment
Client (React) app
Server app
Can be deployed as a single server that outputs both the React client and handles backend server-side logic
isomorphic/universal app

Deployment
const express = require('express');
const path = require('path');
const app = express();
// Serve static files from the React app
app.use(express.static(path.join(__dirname, 'build')));
// API routes
app.get('/api/data', (req, res) => {
res.json({ data: 'Some API data' });
});
// Serve the React app for all other routes
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});

Deployment
How can we ensure our app runs consistently across different environments?
How do we handle dependencies, configurations, and scalability in a seamless way?

Deployment

Docker allows us to package our entire React app, its environment, and dependencies into a container. This ensures that it runs consistently across different environments.

Deployment
# Step 1: Use an official Node.js image as the base image
FROM node:16-alpine
# Step 2: Set the working directory inside the container
WORKDIR /usr/src/app
# Step 3: Copy package.json and package-lock.json to the container
COPY package*.json ./
# Step 4: Install the app dependencies
RUN npm install
# Step 5: Copy the rest of the app's source code to the container
COPY . .
# Step 6: Build the React app for production
RUN npm run build
# Step 7: Use a lightweight web server (nginx) to serve the built app
FROM nginx:alpine
# Step 8: Copy the build output from the previous step to the nginx HTML directory
COPY --from=0 /usr/src/app/build /usr/share/nginx/html
# Step 9: Expose port 80 to allow external traffic to access the app
EXPOSE 80
# Step 10: Start the nginx server
CMD ["nginx", "-g", "daemon off;"]

Deployment


Deployment
When using cloud tools like AWS/GCP, we can provision a Virtual Machine or managed service to run a Docker Container.

Deployment


Deployment
Continuous Integration (CI), is the practice of automatically merging code changes from multiple developers into a single software project immediately after the changes are made.

Deployment

Deployment
-
In a Continuous Integration (CI) process, changes to the codebase are automatically tested and integrated.
-
When a developer commits new changes (e.g., attempting to merge into the 'main' branch), automated testing begins.
-
A CI server monitors the version control repository for new commits.
- When a new commit is detected, the CI server triggers a series of automated tests on the code.

Deployment
Linting/Code Formatting
Unit/Integration/E2E Tests
TypeScript Types
Accessibility Testing

Deployment
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
# Step 1: Checkout the code
- name: Checkout repository
uses: actions/checkout@v2
# Step 2: Set up Node.js (using Node.js 16)
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
# Step 3: Install dependencies
- name: Install dependencies
run: npm install
# Step 4: Run Linting and Code Formatting (ESLint and Prettier)
- name: Lint and Format Code
run: |
npm run lint
npm run format:check
# Step 5: Run TypeScript Type Checking
- name: TypeScript Type Check
run: npm run type-check
# Step 6: Run Unit Tests
- name: Run Unit Tests
run: npm run test
# Step 7: Run Integration and E2E Tests (using Cypress or Jest)
- name: Run Integration and E2E Tests
run: npm run test:e2e
# Step 8: Run Accessibility Tests (example using axe-core or cypress-axe)
- name: Run Accessibility Tests
run: npm run test:accessibility
# Step 9: Build the React app for production (optional)
- name: Build for Production
run: npm run build

Deployment


Deployment


Monitoring
Monitor Requests
Success/Errors
Monitor JS UI Errors

Monitoring


Monitoring


Monitoring


Monitoring
P99: The 99th percentile latency value means that 99% of the requests have a latency equal to or less than this value.
In simpler terms, it represents the worst-case latency for the fastest 99% of the requests. Only 1% of the requests take longer than this time.

Monitoring
P50: The 50th percentile latency value means that 50% of the requests have a latency equal to or less than this value.
The median latency

Monitoring
P50
P90
P95
P99

Monitoring

Logs

Monitoring
poetry add ddtrace
poetry installfrom ddtrace import tracer
from ddtrace.contrib.flask import TraceMiddleware
from flask import Flask
app = Flask(__name__)
traced_app = TraceMiddleware(app, tracer, service="my-service", distributed_tracing=True)
Run the python service with instrumentation enabled

Monitoring
curl -X GET 'localhost:8080/notes'
{}
curl -X POST 'localhost:8080/notes?desc=hello'
( 1, hello)
curl -X GET 'localhost:8080/notes?id=1'
( 1, hello)
curl -X POST 'localhost:8080/notes?desc=newNote'
( 2, newNote)
curl -X GET 'localhost:8080/notes'
{ "1": "hello", "2": "newNote" }
Monitoring


Monitoring
Custom metrics
from datadog import initialize, api
# Set your Datadog API key
options = {
'api_key': '<YOUR_API_KEY>',
'app_key': '<YOUR_APP_KEY>' # Optional, only needed for specific API endpoints
}
# Initialize the Datadog client
initialize(**options)
# Send a custom metric
try:
api.Metric.send(
metric='custom.metric',
points=42, # Value for the metric
tags=['env:production'], # Tags associated with the metric
type='gauge' # Optional, 'gauge' is the default type
)
print("Custom metric sent successfully!")
except Exception as e:
print(f"Error sending custom metric: {e}")

Monitoring
Monitor JS UI Errors

Monitoring


Monitoring


Monitoring
<html>
<head>
<title>Example to send logs to Datadog</title>
<script type="text/javascript" src="https://www.datadoghq-browser-agent.com/datadog-logs-us.js"></script>
<script>
// Set your client token
DD_LOGS.init({
clientToken: '<CLIENT_TOKEN>',
forwardErrorsToLogs: true,
});
// optionally add global metadata attribute--one attribute can be added at a time
DD_LOGS.addLoggerGlobalContext('request_id', <REQUEST_ID>);
</script>
[ … ]
</head>
[ … ]
</html>Automatically collect JavaScript error logs and console logs

Monitoring
DD_LOGS.logger.info('signup button clicked', { env: 'dev', user_id: <USER_ID> })
Also send custom logs

Monitoring
Set up Application Performance Monitoring (APM)
Monitor Error Rates
Track Latency Percentiles (P99, P95, P50)
Capture Client-Side JavaScript Errors
Set Alerts + Downtime Alerts + ping the team/on-call

Front-End Fresh (Session #2)
#1 Try out a design/component library!
Take-home coding exercises
#2 Interact with Context/useReducer
#3 Look into CI/CD OR Datadog

Front-End Fresh (Session #2)
- Building a Personal Brand & Breaking Into FAANG
What's coming next week?

Front-End Fresh (Session #2)
Q/A
The Front-End Fresh Workshop (Oct 2nd)
By djirdehh
The Front-End Fresh Workshop (Oct 2nd)
- 145