No matter what kind of startup you start, it will probably be a stretch for you, the founders, to understand what users want. The only kind of software you can build without studying users is the sort for which you are the typical user.
即使你吃自己的狗粮,结果仍然可能不如意,比如需求提炼不够精确,产品不够惊艳,缺少推广途径等等。NVIDIA CEO 黄仁勋在早年的一次分享中有提到:
If you want to be successful, I would encourage you to develop a tolerance for failure. However, there’s something crucial about failure: if you fail often enough, you might actually become a failure, which is fundamentally different from being successful. So the real question becomes: how do you teach someone to fail, but fail quickly, and change course the moment they recognize a dead end?
The way to approach this is through what we call “intellectual honesty.” This means continuously assessing whether something makes sense, and if it’s the wrong decision, being willing to change your mind.
To achieve this, you have to nurture a tolerance for risk-taking and teach people how to fail—quickly and with minimal cost. Innovation demands a bit of experimentation; experimentation requires exploration, and exploration inevitably involves failure. Without a tolerance for failure, you’d never experiment; without experimentation, you’d never innovate; and without innovation, you’ll never succeed. Otherwise, you’ll just end up being, well… a dweeb.
‘If today were the last day of my life, would I want to do what I am about to do today?’ If the Answer is ‘no’ for too many days in a row, I know I need to change something.
The idea behind affirmations is that you simply write down your goals 15 times a day and somehow, as if by magic, coincidences start to build until you achieve your objective against all odds.
PocketBase: An open-source realtime backend in one file.
Deno: A modern runtime for JavaScript and TypeScript.
This architecture had been running for about 3 years. Mostly satisfied. Zola build fast, its functionality was basically adequate, with Deno, I could write API for frontend page. Using Alpine.js and Tailwind CSS, I could create frontend pages quickly.
However, it wasn’t perfect, mainly for these reasons:
My go-to frontend library is React, but it’s hard to combine React and Zola in an elegant way.
I want to handle HTTP server, Static Site Generation(SSG), and React in a unified way.
This tech stack lacked some features I wanted, such as Server Side Rendering and using Shiki as a code highlighter.
After some investigation, I decided on Astro as my new site engine.
It has all the features I wanted in a meta-framework.
I’ve seen more and more tech influencers mentioning it favorably.
However, making this transition wasn’t easy. On one hand, I was familiar with the current tools and workflow. Though it was missing some features, but it was tolerable. On the other hand, it would take time to become familiar with new tools, build a new workflow, and transfer the content.
After a period of deliberation, I decided to make the switch.
Design
The new site is separated into 3 parts: essays, comments and stream. Essays are articles I write on specific topics, stream is mainly for life activities, like books, photos and thoughts. Kind of facebook homepage, but just for myself.
Essays are written in MDX and generated by Astro. Stream contents are stored in PocketBase and rendered dynamically via Astro’s SSR. These contents are created using PocketBase’s built-in GUI. Comments are also stored in PocketBase, using PocketBase’s API.
Development
Astro’s Developer Experience is really good. It may take some time to get familiar with its directory structure, design philosophy and API. But once you get through this stage, you gain power. Thanks to the well-written docs and GitHub issues, whenever I encountered a problem, there was usually a solution.
I wanted to provide a full-content RSS feed, but MDX files can only be fully rendered within .astro context. Thanks to the Container API (experimental), I nailed it.
I struggled for half a day trying to achieve swap HTML (from server) effects with an .astro component, but it was hard to rebind elements to JS functions. I ended up just using React.
You can’t bind js functions to jsx-like syntax, because they are not real DOM elements, and will be serialized, so js functions can’t survive.
<script is:inline> can make HTML and scripts stay together, but after you replace the HTML with the new content, old scripts won’t work (cause they were bound to the old DOM).
server islands are useful for non-server-related components. They work well for lists, or client-side interactions. When server contents are involved, DOM rebinding becomes an issue.
.astro pages are like PHP, but more frontend-friendly.
.astro components are handy for MDX.
It took me 5 days (working full-time) to design, develop, and transfer content. It wasn’t easy, but as the foundation for the next decade, it was worth the effort.
Deployment
This site is served on a VPS, so I chose Node as the adapter. In theory, it’s simple to deploy: just run pnpm build and node ./dist/server/entry.mjs, then make Nginx point the domain to the port. But reality taught me a lesson. When I ran pnpm build, it took nearly 8 minutes and failed, due to running out of memory.
My first thought was that it allocated too much memory when optimizing images, so I set limitInputPixels: 10000 (100x100) in astro.config.mjs to tell Sharp not to process large images. It didn’t work.
Then I found someone mentioning a command option --experimental-static-build that might fix this scenario, but still, it didn’t work.
When I only left 1 post for Astro to build, it worked, though it still took about 5 minutes.
I was kinda desperate then, even considering rolling back to Zola, which at least wouldn’t cause these issues. This thought lingered for a while before I decided to overcome the problem.
since it could build on my machine, why not just skip the build process on the server and use my machine’s build result? So I put the dist directory in git, and on the server side, I just ran node ./dist/server/entry.mjs. Guess what? it worked! Though it was a bit quirky.
Just as I thought the mission was complete, something else happened. I wanted to write some cache files to the local disk. So I created a .env file, set the cache path, then built and pushed to the server. Server crashed. After digging into the logs, it turned out the cache path was still my machine’s path instead of server’s path defined in the .env file.
The .env file is read at compile time, not runtime. I could remedy it by replacing all the env content before running, but it would be ugly and could lead to potential bugs.
Just when I was at my wit’s end, Bun saved me. Bun is known for its speed and efficiency, and Astro supports Bun, so I decided to give it a try. I ran bun install and bunx --bun astro build. Literally just these two commands, and it worked!
There were still some minor scenarios to handle (e.g. import.meta.env is different when using Bun), but the deployment section basically came to an end. Bun is not only fast but also resource-efficient. In the future, I will prefer Bun over Node.
Conclusion
Astro as a meta-framework fully meets my requirements. PocketBase, combining data storage and CMS in a single file, is really convenient. I hope this new system can help me create more and better content in the future.
As more and more people get both information and entertainment through electronic media, researchers have also studied the extent to which vocabulary is learned through television (Peters & Webb, 2018) and through computer games (Cobb & Horst, 2011; Sundqvist & Sylvén, 2012). They have found that these resources can be effective in expanding vocabulary. More research in this emerging area should help to specify the nature and extent of vocabulary growth via exposure to electronic media. In the meantime, research has overwhelmingly confirmed that vocabulary growth will be limited if the learner does not also engage in focused vocabulary learning activities (Nation, 2001). Jan Hulstijn and Batia Laufer (2001) and others provide evidence that vocabulary development is more successful when learners are fully engaged in activities that require them to attend carefully to the new words and even to use them in productive tasks. Izabella Kojic-Sabo and Patsy Lightbown (1999) found that effort and the use of good learning strategies, such as keeping a notebook, looking words up in a dictionary, and reviewing what has been learned were associated with better vocabulary development. Cheryl Boyd Zimmerman (2009) provides many practical suggestions for teaching vocabulary and also for helping learners to continue learning outside the classroom.
Vocabulary learning occurs because certain conditions are established which facilitate learning. These are repetition, noticing, retrieval, varied encounters and varied use, and elaboration. These learning conditions are themselves underpinned by two key factors:
repetition, i.e. the number of encounters with each word.
the quality of attention at each encounter.
The greater the number of encounters, the more likely learning is to occur; and the deeper the quality of the encounters, the more likely learning is to occur.
Research suggests that there may be little difference in the efficacy of learning smaller and larger sets of words. Instead, they found that students learned most effectively when there was greater spacing between each encounter with the same words. From a practical perspective, this suggests that learning a larger number of words may be more effective than learning a smaller number, because if the words are studied in sequence, there is likely to be a greater amount of time between each word being encountered and studied.
这是 How Languages Are Learned 和 How Vocabulary is Learned 这两本书中提到的关于学习新词汇的要点。 主要是「保持一定时间间隔的重复接触」和「注意力质量」。前者如果每天抽一定时间来听和读,那些中高频的词就有可能被多次遇到;后者表示遇到新单词时要留心,回忆是否之前遇到过,可能是什么意思,或者根据上下文猜测可能的意思,结合字典验证。同时也要有意识地去 Review 新单词。
输出的目的是完成对词汇的 Retrieval(检索),使之能够在正确的场合下被使用。这个过程中如果有人能帮助指出其中的错误自然很好,但如果不能问题也不大。以下是 《How Languages Are Learned》这本书中的描述:
Research on learner beliefs about the role of grammar and corrective feedback in L2 learning confirms that there is often a mismatch between students’ and teachers’ views. In two large-scale studies, Renate Schulz (2001) found that virtually all students expressed a desire to have their errors corrected while very few teachers felt this was desirable. In addition, while most students believed that ‘formal study of the language is essential to the eventual mastery of a [foreign language]’ (p. 254), just over half of the teachers shared this belief. While this may seem surprising, it is consistent with a version of language teaching that is based on the hypothesis that L2 acquisition takes place through exposure to meaningful and comprehensible language. According to this view, explicit instruction and feedback on error play very limited roles.