From 63748ce65b9852e05c53cfae446b8a450b32d44d Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 10:33:53 -0500 Subject: [PATCH 01/13] feat: newsletter on community --- .../newsletters/open-source-is-community.mdx | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/data/newsletters/open-source-is-community.mdx diff --git a/src/data/newsletters/open-source-is-community.mdx b/src/data/newsletters/open-source-is-community.mdx new file mode 100644 index 00000000..1cf2d198 --- /dev/null +++ b/src/data/newsletters/open-source-is-community.mdx @@ -0,0 +1,67 @@ +--- +title: "Contributing to Open Source without being a Jerk" +excerpt: "Open source doesn't work without good faith - and sometimes you need to patch a dependency to do your part." +tags: [open source, javascript, react, devtools] +date: 11-11-2025 +coverImagePublicId: "newsletters/open-source-is-community/cover" +slug: open-source-is-community +--- + +## The Big Idea + +Open source works best when we treat each other like humans -not vending machines for bug fixes. + +## How We Patched a Broken Package - and What It Taught Us + +Recently at [Craftwork](https://craftwork.com), we hit a snag using the [@payloadcms/storage-uploadthing](https://github.com/payloadcms/storage-uploadthing) plugin for image uploads in [Payload CMS](https://payloadcms.com). + +One of our engineers dug in, found the issue, and fixed it. We opened a [pull request](https://github.com/payloadcms/payload/pull/14250), hoping to help others in the same situation. + +But here's the thing: We couldn't afford to wait for the PR to get merged before moving forward. Publishing a forked npm package is a common fallback - but it can be hard to maintain long-term. + +In addition to creating a fork to submit a fix we used [`pnpm patch`](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch) to apply the fix to our repo. It's clean, version-controlled, and works until the upstream package is updated. + + +## Maintainers Aren't the Problem + +It's easy to forget that open source projects are often maintained by small teams under heavy load. At the time of writing, Payload has over 375 open pull requests. Expecting our fix to jump the line would be absurd. + +This isn't about neglect or indifference. It's about capacity. + +In other repos, we've seen PRs sit untouched for months, with long threads of angsty, impatient comments. I get it - but that doesn't help anyone. Especially not the maintainers. + + +### A Better Way to Contribute + +Here's what we did instead: + +- πŸ›  Fixed the issue locally using `pnpm patch` (need a tutorial? I [gotchu](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch)) +- πŸ’¬ Shared the fix in the original GitHub thread to unblock others +- πŸ” Opened a new, cleaner PR +- 🀝 Let the maintainers off the hook β€” no pressure, just help + +This approach makes open source better for _everyone_. It gives control back to contributors, and it gives maintainers room to breathe. + +It is easy to forget that contributing to open source isn't just Pull Requests - discussion, community building, managing expectations, and creative solutions help us all build a better internet together. + +I'm confident you'll get more results if you treat people with humanity - and you will [build a reputation for yourself](https://mikebifulco.com/newsletter/serendipity-isnt-an-accident) that makes people eager to help you when they can. + +--- + +## Want to Do Open Source Better? + +Read [*Working in Public*](https://hardcover.app/books/working-in-public) by Nadia Eghbal. It's the best book I've read on the culture of open sourceβ€”what's broken, what's beautiful, and what's worth fixing. + +Also, if you like learning by watching smart people build in public, check out: + +- [cmgriffing on Twitch](https://www.twitch.tv/cmgriffing) β€” streams dev work and product experiments +- [Rizel Scarlett (aka blackgirlbytes)](https://www.twitch.tv/blackgirlbytes1) β€” open source, conference prep, and community vibes + + +--- + +## Give without the expectation of receiving + +Open source is a special kind of economy. Show up with something useful. Share what you've learned. Make it easier for the next person. + +Let's be better guests in each other's repos. From 5b7aa45e2a96d60a5283ddd59aaa8729efcefd04 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 10:43:54 -0500 Subject: [PATCH 02/13] fix: unsubscribe link --- src/utils/email/templates/EmailLayout.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/utils/email/templates/EmailLayout.tsx b/src/utils/email/templates/EmailLayout.tsx index 11870649..b5c166a3 100644 --- a/src/utils/email/templates/EmailLayout.tsx +++ b/src/utils/email/templates/EmailLayout.tsx @@ -18,12 +18,14 @@ type EmailLayoutProps = { children: React.ReactNode; /** Optional first name for greeting. Set to `false` to disable greeting entirely. */ firstName?: string | false; + includeUnsubscribeLink?: boolean; }; export const EmailLayout = ({ preview, children, firstName, + includeUnsubscribeLink = false, }: EmailLayoutProps) => { const showGreeting = firstName !== false; const greeting = `Hey ${typeof firstName === 'string' ? firstName : 'there'}`; @@ -78,6 +80,19 @@ export const EmailLayout = ({ mikebifulco.com {' '} + {includeUnsubscribeLink && ( + + Not getting what you need? No worries, you can + + {' '} + unsubscribe + {' '} + anytime. + + )} From 24a87b7f8bb7d30e3db63a9c4e9a92cbced5bd39 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 10:44:36 -0500 Subject: [PATCH 03/13] fix: copy --- src/data/newsletters/open-source-is-community.mdx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/data/newsletters/open-source-is-community.mdx b/src/data/newsletters/open-source-is-community.mdx index 1cf2d198..af07db77 100644 --- a/src/data/newsletters/open-source-is-community.mdx +++ b/src/data/newsletters/open-source-is-community.mdx @@ -9,7 +9,7 @@ slug: open-source-is-community ## The Big Idea -Open source works best when we treat each other like humans -not vending machines for bug fixes. +Open source works best when we treat each other like humans - not vending machines for bug fixes. ## How We Patched a Broken Package - and What It Taught Us @@ -19,7 +19,7 @@ One of our engineers dug in, found the issue, and fixed it. We opened a [pull re But here's the thing: We couldn't afford to wait for the PR to get merged before moving forward. Publishing a forked npm package is a common fallback - but it can be hard to maintain long-term. -In addition to creating a fork to submit a fix we used [`pnpm patch`](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch) to apply the fix to our repo. It's clean, version-controlled, and works until the upstream package is updated. +In addition to creating a fork to submit a fix, we used [`pnpm patch`](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch) to apply the fix to our repo. It's clean, version-controlled, and works until the upstream package is updated. ## Maintainers Aren't the Problem @@ -36,7 +36,7 @@ In other repos, we've seen PRs sit untouched for months, with long threads of an Here's what we did instead: - πŸ›  Fixed the issue locally using `pnpm patch` (need a tutorial? I [gotchu](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch)) -- πŸ’¬ Shared the fix in the original GitHub thread to unblock others +- πŸ’¬ Shared the patch tutorial in the GitHub PR to unblock others - πŸ” Opened a new, cleaner PR - 🀝 Let the maintainers off the hook β€” no pressure, just help @@ -57,7 +57,6 @@ Also, if you like learning by watching smart people build in public, check out: - [cmgriffing on Twitch](https://www.twitch.tv/cmgriffing) β€” streams dev work and product experiments - [Rizel Scarlett (aka blackgirlbytes)](https://www.twitch.tv/blackgirlbytes1) β€” open source, conference prep, and community vibes - --- ## Give without the expectation of receiving From 71143266490ba91d24d880b86c83e70465c0e839 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 10:50:17 -0500 Subject: [PATCH 04/13] fix: slightly wider email --- src/utils/email/templates/EmailLayout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/email/templates/EmailLayout.tsx b/src/utils/email/templates/EmailLayout.tsx index b7151951..5280df3e 100644 --- a/src/utils/email/templates/EmailLayout.tsx +++ b/src/utils/email/templates/EmailLayout.tsx @@ -139,7 +139,7 @@ const content = { border: '1px solid rgb(0,0,0, 0.1)', borderRadius: '3px', overflow: 'hidden', - maxWidth: '500px', + maxWidth: '630px', backgroundColor: '#fff', paddingTop: '8px', paddingBottom: '8px', From 5666a18833b3a7222d4ffac5a22a1ef7bc5b2f4f Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 10:53:42 -0500 Subject: [PATCH 05/13] fix: newsletter email footer --- src/utils/email/templates/NewsletterEmail.tsx | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/utils/email/templates/NewsletterEmail.tsx b/src/utils/email/templates/NewsletterEmail.tsx index d505766c..625a50d9 100644 --- a/src/utils/email/templates/NewsletterEmail.tsx +++ b/src/utils/email/templates/NewsletterEmail.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; -import { Column, Markdown, Row } from '@react-email/components'; +import { Column, Link, Markdown, Row, Text } from '@react-email/components'; import { EmailLayout } from './EmailLayout'; @@ -42,6 +41,28 @@ export const NewsletterEmail = ({ {content} + + + + Give 'em hell out there. ✌️
- Mike +
+
+
+ + + + Thanks for reading Tiny Improvements. If you found this helpful, I'd + love it if you{' '} + + share it with a friend + + . It helps me out a great deal! + + + ); }; From 98342c5711e75ef885d8758f05efbfb63594b5a5 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 10:53:42 -0500 Subject: [PATCH 06/13] fix: newsletter email footer --- src/utils/email/templates/NewsletterEmail.tsx | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/utils/email/templates/NewsletterEmail.tsx b/src/utils/email/templates/NewsletterEmail.tsx index d505766c..fe828024 100644 --- a/src/utils/email/templates/NewsletterEmail.tsx +++ b/src/utils/email/templates/NewsletterEmail.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; -import { Column, Markdown, Row } from '@react-email/components'; +import { Column, Link, Markdown, Row, Text } from '@react-email/components'; import { EmailLayout } from './EmailLayout'; @@ -42,6 +41,28 @@ export const NewsletterEmail = ({ {content} + + + + Give 'em hell out there. ✌️
- Mike +
+
+
+ + + + Thanks for reading Tiny Improvements. If you found this helpful, I'd + love it if you{' '} + + share it with a friend + + . It helps me out a great deal! + + + ); }; From 17928f692f9f34baeeba3207457cf60d4387c251 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 10:57:51 -0500 Subject: [PATCH 07/13] fix: signature --- src/utils/email/templates/NewsletterEmail.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/email/templates/NewsletterEmail.tsx b/src/utils/email/templates/NewsletterEmail.tsx index fe828024..91fdc367 100644 --- a/src/utils/email/templates/NewsletterEmail.tsx +++ b/src/utils/email/templates/NewsletterEmail.tsx @@ -27,7 +27,7 @@ type NewsletterEmailProps = { * ``` */ export const NewsletterEmail = ({ - content = '', + content = 'lorem ipsum dolor sit amet this is just sample content for email preview', excerpt = 'Preview text for email clients', }: NewsletterEmailProps) => { return ( @@ -43,16 +43,16 @@ export const NewsletterEmail = ({ - + Give 'em hell out there. ✌️
- Mike
- - Thanks for reading Tiny Improvements. If you found this helpful, I'd - love it if you{' '} + + Thanks for reading πŸ’Œ Tiny Improvements. If you found this helpful, + I'd love it if you{' '} Date: Tue, 11 Nov 2025 10:59:05 -0500 Subject: [PATCH 08/13] fix: sizes --- src/utils/email/templates/EmailLayout.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/utils/email/templates/EmailLayout.tsx b/src/utils/email/templates/EmailLayout.tsx index 5280df3e..7a63253a 100644 --- a/src/utils/email/templates/EmailLayout.tsx +++ b/src/utils/email/templates/EmailLayout.tsx @@ -94,12 +94,7 @@ export const EmailLayout = ({ }} className="mt-2 text-gray-500" > - + Β© {new Date().getFullYear()} • πŸ’Œ Tiny Improvements •{' '} {' '} {includeUnsubscribeLink && ( - + Not getting what you need? No worries, you can{' '} Date: Tue, 11 Nov 2025 11:19:18 -0500 Subject: [PATCH 09/13] feat: email layout --- src/utils/email/templates/EmailLayout.tsx | 19 ++++++++------ src/utils/email/templates/NewsletterEmail.tsx | 26 ++++++++++++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/utils/email/templates/EmailLayout.tsx b/src/utils/email/templates/EmailLayout.tsx index 7a63253a..94a58900 100644 --- a/src/utils/email/templates/EmailLayout.tsx +++ b/src/utils/email/templates/EmailLayout.tsx @@ -35,18 +35,21 @@ export const EmailLayout = ({ {preview} diff --git a/src/utils/email/templates/NewsletterEmail.tsx b/src/utils/email/templates/NewsletterEmail.tsx index 91fdc367..5355f53a 100644 --- a/src/utils/email/templates/NewsletterEmail.tsx +++ b/src/utils/email/templates/NewsletterEmail.tsx @@ -1,10 +1,18 @@ -import { Column, Link, Markdown, Row, Text } from '@react-email/components'; +import { + Column, + Img, + Link, + Markdown, + Row, + Text, +} from '@react-email/components'; import { EmailLayout } from './EmailLayout'; type NewsletterEmailProps = { content: string; excerpt: string; + coverImage?: string; }; /** @@ -29,6 +37,7 @@ type NewsletterEmailProps = { export const NewsletterEmail = ({ content = 'lorem ipsum dolor sit amet this is just sample content for email preview', excerpt = 'Preview text for email clients', + coverImage = 'https://picsum.photos/1200/630', }: NewsletterEmailProps) => { return ( + {coverImage && ( + + + Newsletter cover image + + + )} {content} From 5de6e64d6fcaeee15c965579a4fce3818fdeb7c6 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 11:38:00 -0500 Subject: [PATCH 10/13] fix: cover image url --- scripts/sync-newsletter-to-resend.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/scripts/sync-newsletter-to-resend.ts b/scripts/sync-newsletter-to-resend.ts index d2e18002..44455283 100755 --- a/scripts/sync-newsletter-to-resend.ts +++ b/scripts/sync-newsletter-to-resend.ts @@ -71,11 +71,16 @@ function parseNewsletterDate(dateStr: string | number | Date): Date { /** * Checks if a newsletter should be processed based on its date. */ -function shouldProcessNewsletter(date: string | number | Date, slug: string): boolean { +function shouldProcessNewsletter( + date: string | number | Date, + slug: string +): boolean { try { const newsletterDate = parseNewsletterDate(date); // Newsletter date must be on or after the cutoff date - const shouldProcess = isAfter(newsletterDate, CUTOFF_DATE) || isSameDay(newsletterDate, CUTOFF_DATE); + const shouldProcess = + isAfter(newsletterDate, CUTOFF_DATE) || + isSameDay(newsletterDate, CUTOFF_DATE); if (!shouldProcess) { console.log(`⏭️ Skipping ${slug}: dated before cutoff (${date})`); @@ -97,11 +102,15 @@ function convertImagesToHtml(content: string): string { const imageRegex = /]*\/>/g; return content.replace(imageRegex, (_, publicId) => { - const cloudinaryUrl = `https://res.cloudinary.com/mikebifulco-com/image/upload/${publicId}`; + const cloudinaryUrl = getCloudinaryImageUrl(publicId); return `![](${cloudinaryUrl})`; }); } +const getCloudinaryImageUrl = (publicId: string) => { + return `https://res.cloudinary.com/mikebifulco-com/image/upload/${publicId}`; +}; + /** * Gets list of changed newsletter files in current PR. */ @@ -181,6 +190,9 @@ async function main() { React.createElement(NewsletterEmail, { content: cleanContent, excerpt: frontmatter.excerpt, + coverImage: frontmatter.coverImagePublicId + ? getCloudinaryImageUrl(frontmatter.coverImagePublicId) + : undefined, }) ); From e6bf9688836c10f856a6739df6789c8263686067 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 12:35:23 -0500 Subject: [PATCH 11/13] chore: tweak copy --- src/data/newsletters/open-source-is-community.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/newsletters/open-source-is-community.mdx b/src/data/newsletters/open-source-is-community.mdx index af07db77..b3bcea42 100644 --- a/src/data/newsletters/open-source-is-community.mdx +++ b/src/data/newsletters/open-source-is-community.mdx @@ -54,8 +54,8 @@ Read [*Working in Public*](https://hardcover.app/books/working-in-public) by Nad Also, if you like learning by watching smart people build in public, check out: -- [cmgriffing on Twitch](https://www.twitch.tv/cmgriffing) β€” streams dev work and product experiments -- [Rizel Scarlett (aka blackgirlbytes)](https://www.twitch.tv/blackgirlbytes1) β€” open source, conference prep, and community vibes +- [cmgriffing](https://www.twitch.tv/cmgriffing) streams dev work and product experiments on both Twitch and YouTub +- [Rizel Scarlett](https://www.twitch.tv/blackgirlbytes1) does open source work, conference prep, and interviews on her channel --- From 154b026814e2dafd217ea1c8ff06ff1f9a6ea4b0 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 12:38:19 -0500 Subject: [PATCH 12/13] fix: bg color --- src/utils/email/templates/EmailLayout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/email/templates/EmailLayout.tsx b/src/utils/email/templates/EmailLayout.tsx index 94a58900..bb71b82e 100644 --- a/src/utils/email/templates/EmailLayout.tsx +++ b/src/utils/email/templates/EmailLayout.tsx @@ -55,7 +55,7 @@ export const EmailLayout = ({ {preview} - + {/* Logo Section */}
From 7ab71adc5a39332385ff5dc289e061937d682501 Mon Sep 17 00:00:00 2001 From: Mike Bifulco Date: Tue, 11 Nov 2025 12:51:24 -0500 Subject: [PATCH 13/13] fix: emmies --- src/data/newsletters/open-source-is-community.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/newsletters/open-source-is-community.mdx b/src/data/newsletters/open-source-is-community.mdx index b3bcea42..57ee5986 100644 --- a/src/data/newsletters/open-source-is-community.mdx +++ b/src/data/newsletters/open-source-is-community.mdx @@ -38,7 +38,7 @@ Here's what we did instead: - πŸ›  Fixed the issue locally using `pnpm patch` (need a tutorial? I [gotchu](https://mikebifulco.com/posts/patching-npm-dependencies-with-pnpm-patch)) - πŸ’¬ Shared the patch tutorial in the GitHub PR to unblock others - πŸ” Opened a new, cleaner PR -- 🀝 Let the maintainers off the hook β€” no pressure, just help +- 🀝 Let the maintainers off the hook - no pressure, just help This approach makes open source better for _everyone_. It gives control back to contributors, and it gives maintainers room to breathe. @@ -50,7 +50,7 @@ I'm confident you'll get more results if you treat people with humanity - and yo ## Want to Do Open Source Better? -Read [*Working in Public*](https://hardcover.app/books/working-in-public) by Nadia Eghbal. It's the best book I've read on the culture of open sourceβ€”what's broken, what's beautiful, and what's worth fixing. +Read [*Working in Public*](https://hardcover.app/books/working-in-public) by Nadia Eghbal. It's the best book I've read on the culture of open source: what's broken, what's beautiful, and what's worth fixing. Also, if you like learning by watching smart people build in public, check out: