Skip to content

Commit 59af863

Browse files
committed
Add lists, tables, and highlighter to blue-green-canary-deployment-strategy-comparison articles
1 parent 7a36b9d commit 59af863

8 files changed

Lines changed: 173 additions & 78 deletions

File tree

‎src/components/Diagram/index.astro‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface Props {
88
/** The image source for the diagram */
99
src?: ImageSource | undefined
1010
/** The desktop preview max width token for the diagram image */
11-
size?: '3xl' | '2xl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs' | undefined
11+
size?: 'full' | '3xl' | '2xl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs' | undefined
1212
/** The magnify button corner for the preview image */
1313
magnifyButtonPosition?: 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left' | undefined
1414
/** The title of the diagram */
@@ -18,8 +18,9 @@ export interface Props {
1818
}
1919
2020
const { src, title, description, magnifyButtonPosition = 'top-right', size } = Astro.props as Props
21+
const imageVariant = size === 'full' ? 'full' : 'center'
2122
const imageProps = {
22-
...(typeof size !== 'undefined' ? { size } : {}),
23+
...(typeof size !== 'undefined' && size !== 'full' ? { size } : {}),
2324
...(typeof magnifyButtonPosition !== 'undefined' ? { magnifyButtonPosition } : {}),
2425
}
2526
const hasDefaultSlot = Astro.slots.has('default')
@@ -30,7 +31,7 @@ const hasDefaultSlot = Astro.slots.has('default')
3031
<Image
3132
src={src}
3233
alt={title}
33-
variant="center"
34+
variant={imageVariant}
3435
{...imageProps}
3536
/>
3637
)}

‎src/components/List/__tests__/index.spec.ts‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,40 @@ describe('List (Astro)', () => {
6868
})
6969
).rejects.toThrow('ChatBubbles: invalid size "3xl". Expected one of: lg, xl, 2xl.')
7070
})
71+
72+
test('starts numbering at the provided startNumber and applies the provided color for the numbered-with-background-list variant', async () => {
73+
const List = (await import('@components/List/index.astro')).default
74+
75+
const renderedHtml = await container.renderToString(List, {
76+
props: {
77+
variant: 'numbered-with-background-list',
78+
color: 'info',
79+
startNumber: 4,
80+
items: [
81+
{
82+
lead: 'Step one',
83+
text: 'First item.',
84+
},
85+
{
86+
lead: 'Step two',
87+
text: 'Second item.',
88+
},
89+
],
90+
},
91+
})
92+
93+
await withJsdomEnvironment(async ({ window }) => {
94+
window.document.body.innerHTML = renderedHtml
95+
96+
const badges = Array.from(window.document.querySelectorAll('li > span:first-child'))
97+
const leads = Array.from(window.document.querySelectorAll('li em'))
98+
99+
expect(badges).toHaveLength(2)
100+
expect(leads).toHaveLength(2)
101+
expect(badges[0]?.textContent?.trim()).toBe('4')
102+
expect(badges[1]?.textContent?.trim()).toBe('5')
103+
expect(badges[0]?.className).toContain('bg-info')
104+
expect(leads[0]?.className).toContain('text-info')
105+
})
106+
})
71107
})

‎src/components/List/index.astro‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,18 @@ export type Props = {
3939
wrapper?: string
4040
}
4141
size?: number | 'lg' | 'xl' | '2xl'
42+
color?: string
43+
startNumber?: number
4244
variant: string
4345
style?: Record<string, unknown>
4446
}
4547
46-
const { items, size, variant = 'default', classes, style } = Astro.props
48+
const { items, size, color, startNumber, variant = 'default', classes, style } = Astro.props
4749
const classesProps = classes ? { classes } : {}
4850
const numericSizeProps = typeof size === 'number' ? { size } : {}
4951
const chatBubbleSizeProps = typeof size === 'string' ? { size } : {}
52+
const colorProps = typeof color === 'string' && color.trim().length > 0 ? { color } : {}
53+
const startNumberProps = typeof startNumber === 'number' ? { startNumber } : {}
5054
const styleProps = style ? { style } : {}
5155
const itemsWithColor = items as Array<{
5256
title?: string
@@ -77,7 +81,7 @@ const plainIconItems = items.filter((item): item is Props['items'][number] & { i
7781
{variant === 'check-icons-list' && <CheckIconsList items={items} {...classesProps} {...styleProps} />}
7882
{variant === 'chevron-list' && <ChevronList items={items} {...classesProps} {...styleProps} />}
7983
{variant === 'colored-marker-list' && <ColoredMarkerList items={itemsWithColor} {...classesProps} {...numericSizeProps} {...styleProps} />}
80-
{variant === 'numbered-with-background-list' && <NumberedWithBackgroundList items={items} {...classesProps} {...styleProps} />}
84+
{variant === 'numbered-with-background-list' && <NumberedWithBackgroundList items={items} {...classesProps} {...colorProps} {...startNumberProps} {...styleProps} />}
8185
{variant === 'plain-icon-list' && <PlainIconList items={plainIconItems} {...classesProps} {...numericSizeProps} {...styleProps} />}
8286
{variant === 'side-by-side-list' && <SideBySideList items={items} {...classesProps} {...styleProps} />}
8387
{variant === 'timeline-list' && <TimelineList items={items} {...classesProps} {...styleProps} />}

‎src/components/List/layouts/NumberedWithBackgroundList.astro‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,29 @@ export type Props = {
44
lead?: string
55
text: string
66
}[]
7+
color?: string
8+
startNumber?: number
79
classes?: {
810
ol?: string
911
li?: string
1012
em?: string
1113
}
1214
}
1315
14-
const { items, classes }: Props = Astro.props
16+
const { items, classes, color, startNumber = 1 }: Props = Astro.props
17+
const resolvedColor = typeof color === 'string' && color.trim().length > 0 ? color.trim() : 'primary-offset'
18+
const pillColorClass = `bg-${resolvedColor}`
1519
1620
const olClass = ["space-y-3", classes?.ol]
1721
const liClass = ["flex items-start gap-4 bg-page-offset/50 rounded-lg p-4", classes?.li]
18-
const emClass = ["text-primary-offset font-semibold not-italic", classes?.em]
22+
const emClass = [`text-${resolvedColor}`, "font-semibold not-italic", classes?.em]
1923
---
2024

2125
<ol class:list={olClass}>
2226
{
2327
items.map((item, index) => (
2428
<li class:list={liClass}>
25-
<span class="mt-2 shrink-0 w-8 h-8 bg-primary-offset text-content-inverse rounded-full flex items-center justify-center text-sm font-bold">{index + 1}</span>
29+
<span class:list={["mt-2 shrink-0 w-8 h-8 text-content-inverse rounded-full flex items-center justify-center text-sm font-bold", pillColorClass]}>{startNumber + index}</span>
2630
<div class="pt-1">
2731
{item.lead && <em class:list={emClass} set:html={item.lead}></em>}
2832
<div set:html={item.text}></div>
Loading
Loading

‎src/content/articles/blue-green-canary-deployment-strategy-comparison/index.mdx‎

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,28 @@ Here's the uncomfortable truth most deployment strategy comparisons skip: the "b
2121

2222
Before we dive into the database problem, let's establish baseline understanding of what these strategies actually do.
2323

24-
__Blue/green deployment__ maintains two identical production environments. Blue runs your current version. When you're ready to release, you deploy to green, test it thoroughly, then switch all traffic at once. If something breaks, you switch back to blue.
25-
26-
__Canary deployment__ takes the opposite approach. Instead of switching everyone simultaneously, you route a small percentage of traffic — say, 5%—to the new version. You watch metrics, gradually increase the percentage, and only complete the rollout once you're confident the new version is healthy.
24+
<List
25+
variant="zebra-list"
26+
items={[
27+
{
28+
lead: "Blue/green deployment",
29+
text: "Maintains two identical production environments. Blue runs your current version. When you're ready to release, you deploy to green, test it thoroughly, then switch all traffic at once. If something breaks, you switch back to blue.",
30+
},
31+
{
32+
lead: "Canary deployment",
33+
text: "Takes the opposite approach. Instead of switching everyone simultaneously, you route a small percentage of traffic — say, 5% — to the new version. You watch metrics, gradually increase the percentage, and only complete the rollout once you're confident the new version is healthy.",
34+
},
35+
]}
36+
/>
2737

2838
The fundamental difference comes down to exposure:
2939

3040
<Table
31-
variant="vertical-column-delineation-table"
41+
variant="grid-and-accent-header-table"
42+
fullWidth={false}
43+
classes={{
44+
tbody: "[&_td:last-child]:text-content",
45+
}}
3246
content={{
3347
figure: "Strategy comparison",
3448
thead: { th: ["Aspect", "Blue/Green", "Canary"] },
@@ -80,7 +94,10 @@ Adding a required column breaks old code that can't provide values. Removing a c
8094
The solution is to __never__ deploy schema changes and code changes together. Instead, use a multi-phase approach:
8195

8296
<List
83-
variant="check-icons-list"
97+
variant="side-by-side-list"
98+
classes={{
99+
wrapper: "sm:ml-6 sm:mr-18 mb-6",
100+
}}
84101
items={[
85102
{
86103
lead: "Phase 1 (Expand):",
@@ -97,9 +114,6 @@ The solution is to __never__ deploy schema changes and code changes together. In
97114
]}
98115
/>
99116

100-
101-
<Newsletter />
102-
103117
This pattern works with both strategies, but it's __mandatory__ for canary deployments. Blue/green can sometimes shortcut this with careful timing and instant cutover, but you're accepting more risk.
104118

105119
```sql
@@ -121,12 +135,14 @@ The teams that struggle with deployment strategies are usually the ones trying t
121135

122136
Now that we've established database compatibility as the primary constraint, the decision framework becomes clearer. Here's how to choose.
123137

124-
### Choose Blue/Green When
138+
### Choose Blue / Green When
125139

126140
Your deployments frequently include database migrations that can't easily follow expand-contract patterns. Legacy systems with tightly coupled schemas often fall into this category.
127141

128142
You need instant, complete rollback. Blue/green's traffic switch is as close to "undo" as deployments get. If your downtime tolerance is measured in seconds rather than minutes, this matters.
129143

144+
<Newsletter />
145+
130146
Your team is smaller or less experienced with progressive delivery. Blue/green is conceptually simpler: deploy, test, switch. There's less tooling to maintain and fewer operational scenarios to train for.
131147

132148
You can afford the infrastructure cost. Running two complete environments doubles your compute costs during deployments. For many organizations, this is acceptable for the operational simplicity it provides.
@@ -151,6 +167,10 @@ Many mature organizations don't choose one strategy — they use both. Schema mi
151167

152168
The key insight is that the database constraint drives the decision __per deployment__, not as a blanket policy. A team might deploy three canary releases in a row, then switch to blue/green for a migration, then back to canary. The tooling supports both; the deployment contents determine which to use.
153169

170+
## Conclusion
171+
172+
The deployment strategy debate often focuses on the wrong things — rollback speed, blast radius, tooling sophistication. These matter, but they're secondary to the fundamental question: what are you actually deploying?
173+
154174
<Download
155175
resource="blue-green-canary-deployment-strategy-comparison"
156176
title="Download the Deployment Strategies Guide"
@@ -171,10 +191,6 @@ The key insight is that the database constraint drives the decision __per deploy
171191
]}
172192
/>
173193

174-
## Conclusion
175-
176-
The deployment strategy debate often focuses on the wrong things — rollback speed, blast radius, tooling sophistication. These matter, but they're secondary to the fundamental question: what are you actually deploying?
177-
178194
For database schema changes, blue/green's instant cutover is often the only safe option. For application logic changes, canary's gradual rollout provides better production validation with limited risk. Most real-world systems need both approaches, selected based on what each specific deployment contains.
179195

180196
The best deployment strategy isn't the most technically impressive one. It's the one your team can operate reliably, that matches your database change patterns, and that you'll actually use consistently. Start with the simpler option that works for your constraints, and add sophistication only when you have specific problems that require it.

0 commit comments

Comments
 (0)