教育產品:第二個月
一句話總結
改善我的影片錄製工作流程
重點摘要
- 我學到了幾個技巧,讓我錄製課程影片變得更輕鬆。
- 我決定不需要使用 Merchant of Record(代收代付服務商) 服務。
- 我已將 htmx 納入我製作網頁應用程式的標準工具組中。
目標成績
每個月月初,我都會宣告當月想完成的事。以下是我達成這些目標的情況:
錄製課程中四堂課的可發布版本
- 結果:只錄完一堂課的大部分
- 成績:D
我忘了錄影要花多少時間!而且我也低估了錄影之外的工作量。大多數週我根本沒時間錄影,不過現在我已經漸入佳境了。
我每錄一小時,大約只能產出五到十分鐘可用的影片,而且連續錄製 90 到 120 分鐘後就會精疲力盡。在比較專注的一週,我錄完了一堂完整的 45 分鐘課程,不過現在直播課程已經結束,我應該可以加快速度。
開始販售新版課程
- 結果:改為提供候補名單,而非販售未完成的課程。
- 成績:不適用
我決定暫緩販售新版課程。我原本想推出新版本,並以早鳥優惠價進行宣傳,在我製作新內容的期間,先提供 2020 年的舊版錄影。但我發現很難用俐落的方式來包裝這樣的方案。
我可能無法在我太太生產前完成課程,而寶寶出生後我想請幾個月的育嬰假。我不想在休假期間還覺得欠學生他們已付費購買的其餘教材,因此我決定提供候補名單,而非預購。
改善影片錄製流程
當我為課程錄製影片時,有很多事情可能會中斷錄影,或甚至讓我根本無法開始錄。我一直在努力降低錄製過程中的阻力,讓自己能更快開始錄影,也更不容易受到干擾。
添購桌上型三腳架
在錄影時,我使用放在螢幕上方的 Razer Kiyo 網路攝影機。問題是,要把鏡頭調整到合適的高度,需要費工夫手動調整。

我使用 Razer Kiyo 網路攝影機進行錄製。
我平常會把桌上型螢幕調整到螢幕中心大約與視線齊平。但如果螢幕在視線高度,而網路攝影機又放在螢幕上方,鏡頭就會由上往下俯拍我。
為了取得良好的拍攝角度,我會調整螢幕支架和桌子的高度,讓鏡頭與我的眼睛齊平。但這樣搬動螢幕為錄製增添了許多阻力,而且我無法在每次錄影時都重現完全相同的鏡頭位置。
我用一個小型的桌上型三腳架解決了鏡頭問題:SmallRig VT-20。

我買了一個桌上型三腳架,這樣開始錄影時就不用重新調整桌子或螢幕的位置。
錄影時,我把三腳架放在我和螢幕之間。我用油漆膠帶在桌上標記腳架的位置,這樣每次都能重現相同的位置。
有了三腳架,想錄影時我只要把三腳架和相機放到桌上,位置立刻就正確了。這在降低阻力方面是一大進展,而且我可以用平常的桌面配置來錄影,不必在每次錄影前後費心調整各種高度。
三腳架唯一的缺點是會稍微擋住我看投影片的視線,不過到了錄影階段,我早就已經把內容背熟了。


將網路攝影機架在螢幕上方(左)與放在螢幕前方的桌上型三腳架上(右)的比較。三腳架讓我更容易直視鏡頭。
讓錄製設備保持連接狀態
我以前會在兩次錄影之間把網路攝影機收在抽屜裡,麥克風和支架則放在另一個房間。這意味著每次錄影的前五分鐘,我都在桌上組裝和架設器材。
現在,我盡量讓所有器材保持連接、隨時可錄的狀態。我把麥克風留在桌上並保持連接,也把網路攝影機架在三腳架上,隨時可以放到桌上使用。
以章節為單位錄製
作為課程研究的一部分,我觀看了 Aaron Francis(亞倫·法蘭西斯)的螢幕錄製課程。他的重點比較偏向現場寫程式(live-coding),但仍有足夠多的內容適用於我的課程,因此值得購買。
我覺得亞倫·法蘭西斯的課程很有幫助,但有些最棒的收穫甚至不是他口頭說的內容。光是觀察他如何包裝課程,我就學到了很多。
我的課程第一版由七堂課組成,每堂約 30 到 60 分鐘。亞倫·法蘭西斯的課程長度大致相仿,但他把每堂課細分成只有幾分鐘的獨立章節。

亞倫·法蘭西斯將他的課程分成短小的章節,每個章節只有幾分鐘長。
以學生的角度,我喜歡這種短章節的做法,因為我可以直接跳到最感興趣的章節。
以課程製作者的角度,我也欣賞短章節,因為錄製和剪輯十支四分鐘的影片,比製作一支龐大而笨重的四十分鐘影片要容易得多。而且,在製作影片時,完成五個百分之百完成的章節,感覺比剪到一半的四十分鐘影片好多了。
讓課程內容不受順序限制
我在 2020 年錄製課程時,每支影片開頭都會說類似「歡迎來到第二部分:了解 Hacker News」這樣的話,結尾則會說「在下一支影片中,我會談如何選擇寫作主題。」
在新版錄製中將課程分成章節,自然會產生更多支影片。如果在後製過程中重新排序或刪除影片,就得重做一堆開場和結尾,以維持編號和連結的正確性。
看完亞倫·法蘭西斯的課程後,我發現他的影片從未暗示觀看順序。我決定移除影片中的課程編號,並避免提及任何順序。當我引用另一堂課的內容時,我只會說「我在 Foo 這支影片中有更深入的探討」,而不是說「在後面的影片中」。
買三件一模一樣的 T 恤
亞倫·法蘭西斯課程中的一個建議是在每一堂課中保持一致的外觀。他在所有影片中都穿著素色黑色 T 恤,我覺得這是個不錯的細節。
我原本準備了一件專門的襯衫,每次錄影前換上,結束後再換下來。我原本想,平常穿一件衣服大約 14 小時才會在睡前丟進洗衣籃,這表示同一件衣服應該可以應付大約 14 次一小時的錄影才需要清洗。
結果證明我對衣服發臭的想像完全不準。才錄了三次,衣服就開始變得噁心難受。
所以,我乾脆買了三件一模一樣的 T 恤:

為了保持影片的連續性,我訂了三件同款 T 恤。
這是件簡單的深藍色 Bonobos T 恤,就是我平常會穿的款式。我想,有三件的話,就算有兩件在洗衣籃裡,至少還有一件可以穿。
直播課程中的精彩時刻
我已經完成了部落格課程直播版的全部六堂課,教學過程很有趣,但其中有兩個時刻特別讓我印象深刻。
邀請客座講者
就在我準備課程第一場講座時,我在 Twitter 上看到 Adam Gordon Bell(亞當·戈登·貝爾)在 Twitter 上宣布他被公司資遣了。亞當·戈登·貝爾是 CoRecursive 的主持人,這是我最喜歡的軟體 Podcast 之一。他也是一名部落客,並曾寫出前東家最受歡迎的部落格文章。
亞當·戈登·貝爾曾在我 2020 年開課時的試點學員群中。看到他的行程突然空了下來,我便詢問他是否願意以特別來賓的身分回來參加問答環節,他也慷慨地答應在隔週加入。
時機非常巧合,因為在他加入我的課程「Hit the Front Page of Hacker News」視訊通話的那一刻,亞當·戈登·貝爾剛憑藉他對 Jeffrey Snover(傑佛瑞·史諾佛)的訪談,登上了 Hacker News 的第一名,這位受訪者是 PowerShell 的首席架構師。
這場訪談對我來說很有趣,因為我讀過亞當·戈登·貝爾的許多文章,所以能一窺幕後,聽他分享自己的方法以及這些年來如何調整,令人著迷。
我也從學生身上感受到一股活力。這種熱情程度其實讓我很驚訝,因為那才只是我們的第二堂課,並不是那種「喔,終於換個口味了」的感覺。我最好的解釋是,班上的學生都認識我,但並非每個人都認識亞當·戈登·貝爾,所以看到另一位有實績的人用他自己的觀點來談課程概念,格外有趣。
實際貼文的現場拆解
在課程進行期間,有些學生問我們能否做一個練習,讓大家分享自己的寫作並給予回饋。有三位學生分享了文章或草稿,我們進行了點評,他們問是否能多做一些這樣的練習。
在下一堂課,我開放讓大家自願分享,但沒有人準備好要分享的內容。我提議直接瀏覽 Hacker News,挑一篇文章來分析它的優缺點。這很有趣,但我挑的是首頁上的文章,這表示那些文章已經表現得很好了。班上的同學建議我挑那些剛發布、命運未卜的新投稿。
當我開始點評新投稿時,課程才真正活了起來。因為回頭看一篇成功的投稿並解釋原因很容易,但要看一篇結果未知的投稿並預測它的表現,挑戰就大多了。而且因為是陌生人的貼文,而我又是對著一個私人小組發表,我可以暢所欲言,不必擔心冒犯作者或顯得過於挑剔。
當我閱讀全新的投稿時,班上的回應也更多、參與感也更強。我在課程中已經展示過一些負面範例,但我覺得即時觀看一個人如何因為無聊的開頭或糟糕的結構而對文章失去興趣,是很有趣的。
如何運用這些精彩時刻?
在課堂上經歷每一個精彩時刻後,我都在思考如何更深入地利用它們。我該如何創造更多這樣的時刻,並將其整合到最終的課程中?
對於現場團體問答,除非我開一個擁有八位輪替搭檔的 YouTube 頻道,否則很難讓它變得可重複。但一個不錯的替代方案是與我喜歡的作者進行一對一的影片訪談,我覺得這更容易實現。
專家訪談恰好也是亞倫·法蘭西斯用來推廣課程的手法:

亞倫·法蘭西斯透過發布與 SQLite 領域專家的長篇訪談,來為他即將推出的 SQLite 課程累積關注度。
另一方面,現場拆解則很容易複製。我現在就可以打開相機,一邊閱讀 Hacker News 的投稿,一邊錄下我的即時心得。我不確定這樣是否有娛樂效果,但這是個可以快速、輕鬆實驗的點子。
Merchant of Record 是騙局嗎?
上週,少數支援 Merchant of Record 的支付處理商之一 LemonSqueezy 宣布已被 Stripe 收購。在 Hacker News 的討論串中,一則留言引起了我的注意:
我覺得整個 MoR 的概念就是一種販賣恐懼的行銷手法,目的是讓你多付交易手續費
99% 的 SaaS 根本達不到需要 MoR 的 MRR 門檻
那 1% 突破七位數 MRR 的公司,大可直接聘請內部人員來處理稅務繳納,也不用讓客戶收到印有 MoR 品牌的發票而感到困惑
我詢問了我的會計師,他確認對於美國大多數州而言,我需要達到一定的門檻才會對數位產品負有銷售稅義務,依州而異,大約是每年 10 萬美元或 100 筆交易。
在像加州或紐約這樣人口眾多的州,門檻則是 50 萬美元。如果我達到需要跨越夠多州的門檻、多到繳銷售稅變得很麻煩的程度,那時我已經靠銷售賺進數十萬美元,大可直接聘請會計師來處理。
我原本計畫在 Gumroad 上販售,主要就是看上它的 Merchant of Record 功能。但 Gumroad 要收取 10% 的佣金,而且這還不包含金流處理費。
鑑於我在麻薩諸塞州以外的州達到銷售稅門檻的可能性極低,這意味著我可以在 Gumroad 以外的地方販售課程,省下那 10% 的費用。
業餘專案
找到我偏好的 htmx 表單模式
在上次的回顧中,我提到我已經開始使用 htmx 而且很喜歡它,但覺得它的錯誤處理有點彆扭。
舉例來說,這是 ScreenJournal(我的電影評論網頁應用程式)上的一個表單。

來自 ScreenJournal 網頁應用程式的簡單 HTML 表單。
當使用者提交表單時,只會有兩種結果:
- 設定已成功儲存。
- 處理請求時發生錯誤。
htmx 慣用的處理方式是,當使用者提交表單時,伺服器會回傳整個表單的 HTML,並填入使用者提交的值以及新增的成功或錯誤訊息。
我覺得 htmx 推薦的表單模式很彆扭又容易出錯。瀏覽器已經正確填好了表單,為什麼伺服器還要把整個表單回傳給瀏覽器重新渲染?唯一的新資訊就是成功訊息或錯誤訊息,為什麼伺服器不能只傳送那些就好?
以下是我對上述 ScreenJournal 表單稍微調整後的 htmx 模式,讓 htmx 的功能更輕量:
<form
hx-put="/account/notifications"
hx-clear="#result-success, #result-error"
hx-disabled-elt="input, .btn"
hx-target="#result-success"
hx-target-error="#result-error"
hx-swap="textContent"
>
<label for="new-reviews-checkbox">
Email me when users post reviews
</label>
<input
type="checkbox"
id="new-reviews-checkbox"
name="new-reviews"
/>
<label for="all-comments-checkbox">
Email me when users add comments
</label>
<input
type="checkbox"
id="all-comments-checkbox"
name="all-comments"
/>
<button value="Save">Save</button>
<div role="status">
<span>Loading...</span>
</div>
</form>
<div id="result-success" role="alert"></div>
<div id="result-error" role="alert"></div>重點都在 <form> 標籤中,因此我來逐項說明發生了什麼事:
hx-put="/account/notifications"當使用者提交表單時,對伺服器上的 /account/notifications 路由發送 HTTP PUT 請求。
hx-clear="#result-success, #result-error"當使用者提交表單時,清空 ID 為 result-success 或 result-error 的元素的內容。
這個屬性並非 htmx 內建,而是來自我自己撰寫的自訂擴充功能 clear-before-send。我將它與 CSS 規則 .alert:empty { display: none } 搭配使用,以便在 <div> 標籤帶有 alert 類別且沒有文字內容時將其隱藏。
hx-disabled-elt="input, .btn"在 HTTP 請求期間,停用所有 <input> 標籤以及帶有 .btn CSS 類別的元素,讓使用者無法重複提交同一個請求。
附註:htmx 文件暗示 hx-disabled-elt="this" 應該會停用整個表單,但似乎沒有作用。作為權宜之計,我必須使用能選取表單中所有輸入欄位的選擇器。
hx-target="#result-success"如果伺服器回應的狀態碼在 200 範圍內,就將伺服器回應的主體放入 ID 為 result-success 的元素中。
hx-target-error="#result-error"如果伺服器回應的狀態碼不在 200 範圍內,就將伺服器回應的主體放入 ID 為 result-error 的元素中。
這個屬性並非 htmx 核心函式庫的一部分,而是來自一個名為 response-targets 的 htmx 擴充功能。
hx-swap="textContent"在填入 hx-target 或 hx-target-error 的目標時,htmx 應該替換目標元素的 textContent(而非替換其內部或外部的 HTML)。
以下是成果。我已調整開發伺服器,讓它等待兩秒才回應,且每隔一次的請求會以錯誤訊息失敗:
ScreenJournal 上的通知管理畫面
需要注意的重點有:
- 當請求正在傳送時,htmx 會停用表單。
- 只要使用者再次點擊「Save」,先前的成功/錯誤訊息就會消失。
- 成功與錯誤訊息有不同的樣式。
因此,不必為每個表單撰寫客製化的 JavaScript,就能實現相當多的功能。我希望這本來就是 HTML 演進的方向,而不是需要靠第三方函式庫來實現如此常見的功能,但我很開心 htmx 填補了這個缺口。
總結
完成了什麼?
經驗教訓
- 尋找機會,降低錄製影片過程中的阻力。
- 我可以透過發布與優秀部落客的訪談以及部落格文章的拆解來推廣課程。
- 美國大多數獨立創業者可能不需要 Merchant of Record。
下個月的目標
- 完成課程錄製。
- 開始販售課程。
隨機一篇部落格