标签 技术选型 下的文章

很多人以为在大厂工作,就是不停地写代码、解决技术难题。

但事实是:真正成功的工程师并不是那些代码写得最好的人,而是那些解决了代码以外事情的人。

本篇和你分享 21 条职场教训。

这些教训,有的能让你少走几个月的弯路,有的则需要数年才能完全领悟。

它们都与具体的技术无关,因为技术变化太快,根本无关紧要。

但这些教训,项目换了一个又一个,团队换了一批又一批,始终在重复上演。

希望能帮助到你:

1. 最优秀的工程师都痴迷于解决用户问题

很多人容易爱上一项新技术,然后到处找地方用它。

我干过,你肯定也干过。

但真正创造最大价值的工程师是反过来的:

他们专注于深入理解用户问题,并让解决方案从这种理解中自然而然地涌现。

以用户为中心意味着花时间处理支持工单,与用户沟通,观察用户遇到的困难,不断追问“为什么”,直到找到问题的症结所在。

真正理解问题的工程师往往会发现,优雅的解决方案比任何人预想的都要简单。

工程师如果一开始就想着如何解决问题,往往会为了寻找理由而人为地增加复杂性。

2. 正确很容易,共同达成正确才是真正的挑战

即使你在技术上胜券在握,最终也可能输掉项目。

我曾亲眼目睹一些才华横溢的工程师,自诩为房间里最聪明的人,但总是默默地积攒怨气。最终表现为“莫名其妙的执行问题”和“莫名其妙的阻力”。

关键不在于证明自己正确,而在于参与讨论以达成对问题的共识。

为他人创造发言空间,并对自己确信的观点保持怀疑。

3. 行动优先,先做,再做对,再做好

追求完美会让人停滞不前。

我曾经见过工程师花几周讨论一个从没建过的东西的理想架构。

但完美的方案很少从思考中产生,它都是从与现实的碰撞中产生。

先做出来,再做对,再做得更好。

把丑陋的原型放到用户面前,写出乱糟糟的技术文档初稿,发布那个让你有点尴尬的 MVP。

从真实反馈中学到的内容,哪怕只有一周,也远比一个月的理论辩论多得多。

4. 代码清晰远比炫技重要

我知道你想要写出酷炫的代码,那可以证明自己很牛逼。

但项目往往不止你一个人,以后还有其他同事要维护。

优化时要考虑他们的理解能力,而不是你的代码是否优美。

5. 谨慎选择新技术

新技术就像贷款,你要用 bug、招聘困难和认知负担来还。

关键不在于“永远不要创新”,而在于“只在因创新可以带来独特报酬的领域进行创新”。其他的一切还是应该回归平庸。

6. 你的代码不会替你说话,但人会

刚开始工作时,我相信是金子总会发光。

但我错了。

代码静静地躺在仓库里。你的领导在会议上提到你,或者没提。同事推荐你参与项目,或者推荐了别人。

在大公司,决策是在你没被邀请的会议上做出的,用的是你没写的总结,由只有五分钟时间和十二件事要处理的人做出的。

如果你不在场时没人能清楚说出你的价值,那你的价值就等于可有可无。

这不是让你鼓吹自己,而是告诉你:你需要让你的价值被所有人看到。

7. 最好的代码是你根本不用写的代码

工程师文化崇拜创造。

没有人会因为删除代码而获得晋升,即使删除代码往往比添加代码更能改进系统。

因为你不写的每一行代码,都意味着你永远不必调试、维护或解释。

在动工之前,先仔细思考一下:“如果我们不做这件事会发生什么?” 有时答案是“没什么坏处”,那就是你的解决方案。

问题不是工程师不会写代码,而是我们太会写了,以至于忘了问:该不该写?

8. 大规模时,连你的 bug 都有用户

用户多的时候,连你的 bug 都会有用户,这产生了一个职业级洞察:

你不能把兼容性工作当“维护”,把新功能当“真正的工作”。兼容性就是产品。

所以把你的“废弃”做成“迁移”,带上时间、工具和同理心。

9. 慢实际上是因为不协调

项目进展缓慢时,人们的第一反应往往是责怪执行:员工不够努力、技术不成熟、工程师人手不足。

但通常来说,这些都不是真正的问题所在。

在大公司,团队是并发执行的基本单位,但随着团队数量的增加,协调成本呈几何级增长。

大多数效率低下实际上源于目标不一致——人们在做错误的事情,或者以不兼容的方式做正确的事情。

所以高级工程师花更多时间澄清方向、接口和优先级,而不是“写代码更快”,那些才是真正的瓶颈所在。

10. 专注你能控制的,忽略你无法控制的

在大公司,无数的变数都超出你的掌控——组织架构调整、管理决策、市场变化、产品转型等等。

过度关注这些因素只会让你焦虑不安,却又无能为力。

所以高效的工程师,会锁定自己的影响圈。你控制不了是否会重组,但你能控制工作质量、如何应对、学到什么。

这并非被动接受,而是策略性关注。

把精力浪费在无法改变的事情上,就等于浪费了原本花在可以改变的事情上的精力。

11. 抽象并不能消除复杂性

每一次抽象都是一种赌博,赌你不需要理解下面是什么。

有时候你会赢,但总会有漏洞,一旦出现漏洞,你就需要清晰地知道你站在什么上面。

所以高级工程师即使技术栈越来越高,也要持续学习“更底层”的东西。

12. 写作让表达更清晰,以教带学是最快的学习方式

写作能带来更清晰的表达。

当我向别人解释一个概念——在文档里、演讲中、代码评审评论里、甚至和 AI 聊天,我都会发现自己理解上的不足。

所以如果你觉得自己懂了什么,试着简单地解释它。卡住的地方,就是你理解肤浅的地方。

13. 注重粘合性工作

粘合性工作——例如写文档、帮新人上手、跨团队协调、流程优化——至关重要。

但如果你总是无意识地做这些,反而可能会拖慢技术成长,把自己累垮。

陷阱在于把它当“乐于助人”的活动,而不是当作有边界的、刻意的、可见的影响力。

尝试给它设时限,轮换做,把它变成产出物:文档、模板、自动化。

让它作为“影响力”被看见,而不是作为“性格特点”。

14. 如果你赢得每一场辩论,你很可能是在积累无声的阻力

当人们不再和你争,不是因为你说服了他们,而是因为他们放弃了。

但他们会在执行中表达分歧,而不是在会议上。

所以真正的共识需要更长时间。你得真正理解别人的观点,吸收反馈,有时候需要你当众改变主意。

短期“我是对的”的快感,远不如长期和心甘情愿的合作者一起建设的现实来得珍贵。

15. 当衡量标准变成目标时,它就停止了衡量

你暴露给管理层的每个指标,最终都会被博弈。

不是因为恶意,而是因为人会优化被度量的东西。

追如果你追踪代码行数,你会得到更多的代码行数。如果你追踪开发速度,你会得到过高的估算值。

高手的做法是:对每个指标请求都提供一对指标。一个用于衡量速度,一个用于衡量质量或风险。然后,坚持解读趋势,而不是盲目追求阈值。

目标是洞察,而非监控。

16. 承认自己不知道的事情比假装自己知道更能带来安全感

资深工程师说“我不知道”并不是示弱——他们是在鼓励大家坦诚面对。

当领导者承认自己的不确定性时,就等于在暗示其他人也可以这样做。如果不这样的话,就会形成一种人人假装理解、问题被掩盖直到爆发的文化。

我见过团队里最资深的人从不承认自己不明白,我也见过由此造成的后果。问题不被问出来,假设不被挑战,初级工程师保持沉默因为他们以为别人都懂。

17. 你的人脉关系比你拥有的任何一份工作都更长久

职业生涯早期,我专注于工作本身,忽视了人脉经营。回头看,这是个错误。

那些注重人脉关系的同事,在接下来的几十年里都受益匪浅。他们最先了解机会,更快地建立人脉,获得职位推荐,和多年来建立信任的人一起创业。

你的工作不会永远持续下去,但你的人脉网络却会一直存在。

以好奇心和慷慨的态度去拓展人脉,而不是抱着功利主义的心态。

当需要向前迈进的时候,往往是人际关系打开了这扇门。

18. 大多数绩效的提升来自于减少工作量

当系统变慢时,人们的第一反应往往是加东西:加缓存、并行处理、使用更智能的算法。

有时候这样做是对的。

但我发现,通过询问“我们计算了哪些不必要的东西?”往往能带来更多性能提升。

删除不必要的工作几乎总是比更快地完成必要的工作更有成效。最快的代码是永远不会运行的代码。

所以在进行优化之前,先问问自己这项工作是否真的应该存在。

19. 流程存在的目的是为了减少不确定性,而不是为了留下书面记录

最好的流程是让协调更容易、让失败成本更低。

最差的流程是官僚主义——它的存在不是为了帮忙,而是为了出事时推卸责任。

如果你无法解释一个个流程如何降低风险或提高清晰度,那么它很可能只是增加了额外开销。

如果人们花在记录工作上的时间比做工作的时间还多,那就说明出了大问题。

20. 最终,时间会比金钱更有价值

刚开始工作的时候,你用时间换钱——这没问题。

但到了某个阶段,情况就完全不同了。你会开始意识到,时间才是不可再生资源。

我见过一些高级工程师为了晋升而累垮自己,只为了多拿几个百分点的薪酬。有些人确实升职了,但事后大多数人都在反思,自己放弃的一切是否值得。

答案不是“别努力工作”,而是“知道你在交易什么,并深思熟虑地进行交易”。

21. 没有捷径,但有复利

专业技能源于刻意练习——略微超越现有水平,然后不断反思,不断重复。年复一年,没有捷径可走。

但令人欣慰的是:学习的进步在于创造新的选择,而不仅仅是积累新的知识。

写作——不是为了吸引眼球,而是为了清晰表达。构建可复用的基础模型。将过往的经验总结成行动指南。

所以如果工程师把职业生涯看作是复利投资,而不是彩票,那么他最终往往会取得更大的成就。

22. 最后

21 条听起来很多,但它们可以归结为几个核心点:保持好奇,保持谦逊,记住工作始终是关于人的——你的用户、你的队友。

工程师的职业生涯足够长,可以犯很多错误。我最钦佩的工程师,不是那些什么都做对的人——而是那些从错误中学习、分享发现、并坚持不懈的人。

本篇整理自《21 Lessons From 14 Years at Google》,希望能帮助到你。

我是冴羽,10 年笔耕不辍,专注前端领域,更新了 10+ 系列、300+ 篇原创技术文章,翻译过 Svelte、Solid.js、TypeScript 文档,著有小册《Next.js 开发指南》、《Svelte 开发指南》、《Astro 实战指南》。

欢迎围观我的“网页版朋友圈”,关注我的公众号:冴羽(或搜索 yayujs),每天分享前端知识、AI 干货。

Nuxt 3 vs Next.js:新手选型指南与项目实战对比

当Vue遇上React,服务端渲染框架如何选择?

在现代Web开发中,两大全栈框架Nuxt 3和Next.js占据着服务端渲染(SSR)领域的主导地位。它们都提供了文件系统路由、自动代码分割、SEO优化等现代Web应用所需的核心功能,但技术选型背后的技术栈差异设计哲学却大不相同。

本文将通过对比分析,帮助前端新手理解这两大框架的区别,并提供实际的项目创建示例。


01 核心差异:Vue与React的技术栈选择

Nuxt 3与Next.js最根本的区别在于其底层技术栈

  • Nuxt 3:基于Vue 3生态系统,采用组合式API和响应式系统
  • Next.js:基于React生态系统,支持最新的React特性

这种核心差异决定了你的开发体验、学习曲线以及可用的第三方库生态。

学习曲线对比

对于完全没有前端经验的新手来说,Vue通常被认为比React学习曲线更平缓。Vue的模板语法更接近传统HTML,而React的JSX则需要适应将HTML与JavaScript混合编写的模式。

框架特性Nuxt 3Next.js
基础框架Vue 3React
路由系统文件系统路由(pages/目录)文件系统路由(app/目录)
数据获取useAsyncData, useFetch服务端组件、fetch API
状态管理Pinia (推荐)Zustand, Redux等
样式方案多种选择(CSS模块、Tailwind等)多种选择(CSS模块、Tailwind等)
部署平台Vercel、Netlify、Node服务器等Vercel(官方)、Netlify等

生态圈对比

Next.js拥有更庞大的社区和更丰富的第三方库,这得益于React本身的普及度。Nuxt 3虽然社区规模较小,但其官方模块质量很高,且与Vue生态无缝集成。


02 快速入门:创建你的第一个应用

Nuxt 3入门示例

项目初始化

# 创建Nuxt 3项目
npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev

创建页面和组件

  1. pages/index.vue中创建主页:

    <template>
      <div class="container">
     <h1>欢迎使用Nuxt 3</h1>
     <p>当前时间:{{ currentTime }}</p>
     <button @click="refreshTime">刷新时间</button>
      </div>
    </template>
    
    <script setup>
    // 使用组合式API
    const currentTime = ref('')
    
    // 获取服务器时间
    onMounted(async () => {
      const { data } = await useFetch('/api/time')
      currentTime.value = data.value
    })
    
    // 客户端交互
    const refreshTime = () => {
      currentTime.value = new Date().toLocaleString()
    }
    </script>
  2. 创建API端点server/api/time.get.ts

    export default defineEventHandler(() => {
      return new Date().toISOString()
    })

Next.js入门示例

项目初始化

# 创建Next.js项目(使用App Router)
npx create-next-app@latest my-next-app
cd my-next-app
npm install
npm run dev

创建页面和组件

  1. app/page.tsx中创建主页:

    export default function HomePage() {
      return (
     <div className="container">
       <h1>欢迎使用Next.js</h1>
       <TimeDisplay />
     </div>
      )
    }
    
    // 服务端组件:自动在服务器上运行
    async function TimeDisplay() {
      // 在服务端获取数据
      const response = await fetch('http://worldtimeapi.org/api/timezone/Asia/Shanghai')
      const data = await response.json()
      
      return (
     <div>
       <p>当前时间:{data.datetime}</p>
       <ClientComponent />
     </div>
      )
    }
    
    // 客户端组件:需要"use client"指令
    'use client'
    function ClientComponent() {
      const [count, setCount] = useState(0)
      
      return (
     <button onClick={() => setCount(count + 1)}>
       点击次数:{count}
     </button>
      )
    }

03 特性深度对比:数据获取与渲染策略

数据获取方式对比

Nuxt 3的数据获取

<template>
  <div>
    <h2>文章列表</h2>
    <div v-if="pending">加载中...</div>
    <ul v-else>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
  </div>
</template>

<script setup>
// useAsyncData用于服务端获取数据
const { data: posts, pending } = await useAsyncData(
  'posts',
  () => $fetch('https://api.example.com/posts')
)

// useFetch是useAsyncData的简写
const { data: user } = await useFetch('/api/user')
</script>

Next.js的数据获取

// 在App Router中,页面组件默认为服务端组件
export default async function PostsPage() {
  // 直接使用fetch API,Next.js会自动优化
  const response = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 } // 每60秒重新验证
  })
  const posts = await response.json()
  
  return (
    <div>
      <h2>文章列表</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
      <LikeButton postId={posts[0].id} />
    </div>
  )
}

// 客户端交互组件需要"use client"指令
'use client'
function LikeButton({ postId }) {
  const [likes, setLikes] = useState(0)
  
  return (
    <button onClick={() => setLikes(likes + 1)}>
      点赞 ({likes})
    </button>
  )
}

渲染策略对比

两个框架都支持多种渲染策略,但实现方式不同:

渲染模式Nuxt 3实现Next.js实现
静态生成(SSG)nuxt generateoutput: 'static'
服务端渲染(SSR)默认启用默认启用(服务端组件)
客户端渲染(CSR)<ClientOnly>组件"use client"指令
增量静态再生(ISR)通过模块实现原生支持(fetch选项)

04 实际应用场景分析

何时选择Nuxt 3?

  1. Vue技术栈项目:团队已熟悉Vue生态
  2. 快速原型开发:需要快速搭建MVP产品
  3. 内容型网站:博客、文档、营销页面
  4. 项目结构清晰:喜欢"约定优于配置"的理念

Nuxt 3优势场景示例

<!-- 快速创建SEO友好的内容页面 -->
<template>
  <div>
    <Head>
      <Title>产品介绍 - 我的网站</Title>
      <Meta name="description" :content="product.description" />
    </Head>
    
    <article>
      <h1>{{ product.title }}</h1>
      <!-- 内容自动渲染 -->
      <ContentRenderer :value="product" />
    </article>
  </div>
</template>

<script setup>
// 自动根据文件路径获取内容
const { data: product } = await useAsyncData('product', () => 
  queryContent('/products').findOne()
)
</script>

何时选择Next.js?

  1. React技术栈项目:团队已熟悉React生态
  2. 大型复杂应用:需要React丰富生态支持
  3. 需要最新特性:希望使用React最新功能
  4. Vercel平台部署:计划使用Vercel的完整能力

Next.js优势场景示例

// 复杂的动态仪表板应用
export default async function DashboardPage() {
  // 并行获取多个数据源
  const [sales, users, analytics] = await Promise.all([
    fetchSalesData(),
    fetchUserData(),
    fetchAnalyticsData(),
  ])
  
  return (
    <div className="dashboard">
      <SalesChart data={sales} />
      <UserTable users={users} />
      <AnalyticsOverview data={analytics} />
      {/* 实时更新的客户端组件 */}
      <LiveNotifications />
    </div>
  )
}

// 使用React Server Components实现部分渲染
'use client'
function LiveNotifications() {
  const [notifications, setNotifications] = useState([])
  
  useEffect(() => {
    // 建立WebSocket连接获取实时数据
    const ws = new WebSocket('wss://api.example.com/notifications')
    // ... 处理实时数据
  }, [])
  
  return <NotificationList items={notifications} />
}

05 开发体验与工具链对比

Nuxt 3的开发体验

  1. 零配置起步:大多数功能开箱即用
  2. 模块系统:官方和社区模块质量高
  3. TypeScript支持:一流的TypeScript体验
  4. 开发工具:Nuxt DevTools提供强大调试能力
# Nuxt 3的典型工作流
npx nuxi@latest init my-project  # 创建项目
npm install                       # 安装依赖
npm run dev                       # 开发模式
npm run build                     # 生产构建
npm run preview                   # 预览生产版本

Next.js的开发体验

  1. 灵活的配置:可根据需要深度定制
  2. TurboPack:极快的构建和刷新速度
  3. 完善的文档:官方文档质量极高
  4. Vercel集成:无缝部署和预览体验
# Next.js的典型工作流
npx create-next-app@latest my-app  # 创建项目
npm install                        # 安装依赖
npm run dev                        # 开发模式
npm run build                      # 生产构建
npm run start                      # 启动生产服务器

06 性能与优化对比

性能特征

  1. 首次加载性能:两者都优秀,Nuxt 3在小型项目上可能略快
  2. 开发服务器速度:Next.js的Turbopack在大型项目上优势明显
  3. 构建速度:取决于项目大小,两者都提供增量构建

优化技巧对比

Nuxt 3优化示例

<!-- 组件懒加载和图片优化 -->
<template>
  <div>
    <!-- 延迟加载重型组件 -->
    <LazyMyHeavyComponent v-if="showComponent" />
    
    <!-- 自动优化的图片 -->
    <NuxtImg
      src="/images/hero.jpg"
      width="1200"
      height="600"
      loading="lazy"
      format="webp"
    />
  </div>
</template>

Next.js优化示例

// 使用Next.js内置优化功能
import Image from 'next/image'
import dynamic from 'next/dynamic'

// 动态导入重型组件
const HeavyComponent = dynamic(() => import('./HeavyComponent'))

export default function OptimizedPage() {
  return (
    <>
      {/* 自动优化的图片组件 */}
      <Image
        src="/hero.jpg"
        alt="Hero image"
        width={1200}
        height={600}
        priority={false} // 非关键图片延迟加载
      />
      
      {/* 条件加载重型组件 */}
      <HeavyComponent />
    </>
  )
}

07 新手选择建议

根据背景选择

  1. 完全零基础

    • 如果喜欢更直观的模板语法 → 选择Nuxt 3
    • 如果看重就业市场需求 → 选择Next.js
  2. 有前端基础

    • 熟悉HTML/CSS/JS → 都可尝试,根据偏好选择
    • 有React经验 → 选择Next.js
    • 有Vue经验 → 选择Nuxt 3

根据项目类型选择

项目类型推荐框架理由
个人博客/作品集Nuxt 3快速搭建,SEO优秀
企业官网/营销页Nuxt 3开发效率高,维护简单
SaaS/管理后台Next.jsReact生态丰富,组件库多
电商平台Next.js性能优化完善,生态成熟
实时应用均可根据团队技术栈选择

无论选择哪个框架,最重要的是开始构建。真正的经验来自于项目实践,而不是框架比较。

🗳️ 互动时间:你的选择是?

读完全文,相信你对 Nuxt 3 和 Next.js 有了更清晰的认识。技术选型没有标准答案,真实项目中的经验才是最宝贵的参考。

欢迎在评论区分享你的观点:

  1. 投票选择:你目前更倾向于或正在使用哪个框架?

    • A. Nuxt 3 (Vue阵营)
    • B. Next.js (React阵营)
    • C. 两个都在用/观望中
  2. 经验分享:在实际项目中,你使用 Nuxt 3 或 Next.js 时,遇到的最大挑战或最惊喜的体验是什么? 你的分享对其他开发者会非常有帮助!

关注我的公众号" 大前端历险记",掌握更多前端开发干货姿势!

本文由mdnice多平台发布

开发软件就像一次旅行,在这个过程中,团队需要不断做出决策,既包括他们所构建产品的功能(即 MVP,最小可行产品),也包括支撑该 MVP 所需的架构(即 MVA,最小可行架构)。

 

采用这种方法的主要挑战在于,我们必须足够快速地构建出可发布的产品,以便团队能尽快获得关键反馈。

 

当团队寻求更快捷的获取反馈的方式时,他们必须决定,是选择一条他人已经走过的路径,还是另辟蹊径、自行探索。

 

有一种重用架构的方式,那就是使用其他团队已经采用的相同的平台或框架。一个优秀的平台或框架能让每个团队专注于自身独有的“增值部分”。重复造轮子毫无价值,因此忽视现有的平台和框架团队,实际上就是在浪费精力,未能聚焦于只有他们自己才能完成的事情。

 

平台和框架就像已经铺设好的道路,它们能够帮助团队在开发旅程中更快地前进,并提供定义明确的“出口匝道”或扩展点,让团队可以在需要时对平台进行扩展以满足自身的需求。但它们也附带一些副作用,可能使其变得不够理想。

 

团队需要针对如下问题做出判断,即何时(如果有的话)应当离开他人铺设的道路,通过扩展平台/框架,甚至开发全新的平台/框架,走出自己的路。

 

当团队以平台或框架作为其软件架构的基础时,所面临的挑战在于,选择一条最接近其目标目的地的“铺好的路”(即平台或框架),同时尽量减少绕行或新建工程。但是,MVP 的问题是这个“目的地”在项目初期往往是未知的。

平台和框架会替你做出许多决策,但其中有些是你根本不需要的

平台和框架通常具有一定的倾向性,这意味着在构建 MVA 时,团队需要做的架构决策更少。关键问题在于,团队能否接受平台开发者所做的那些决策?理想情况下,团队应审视自身所需的架构决策,并与平台已做出的决策进行对比。

 

这带来了两个重要的挑战:

  1. 团队往往会在实验获得的经验反馈中,逐步发现他们真正需要做哪些决策;

  2. 平台开发者所做的决策并不总是明确或最终的,尤其当平台提供了扩展点,要求使用团队自行填充代码时更是如此。在“铺好的路”这一隐喻中,这些扩展点正是团队可以偏离主路、走上自己方向的地方。

 

许多平台的决策是无害的,只要不影响团队必须满足的质量属性需求(QAR),就可以接受甚至忽略。判断这些决策是否会造成损害的唯一方法,就是通过实验暴露平台在哪些方面未能达成系统的目标。由于平台开发者所做的决策常常未被记录,甚至是未知的,所以团队必须要测试他们的系统(包括所依赖的平台),以确保架构目标(即 QAR)得以实现。

 

即使技术上可行,扩展平台或框架也可能非常复杂。其他使用该平台或框架的人可能不同意你所提出的决策及相关变更,或者他们可能更偏好其他方案,而这些方案又无法满足某些团队的实际需求。这也是为什么存在如此多功能相似的平台和框架的原因之一。

 

此外,当团队决定扩展某个平台或框架时,他们实际上做出了一个隐含承诺,也就是长期维护这些扩展。他们必须将这种成本和所需的时间/精力纳入决策考量。这包括未来升级应用以适配平台/框架新版本的成本和工作量;若不及时升级,可能导致应用崩溃、安全漏洞无法修复,也无法利用新版本在性能和可扩展性方面的改进。

平台和框架能节省时间,直到它们无法做到这一点为止

在我们的简化视角中,平台是指应用程序运行所使用的软件环境(以及提供支撑的基础设施),平台的一个样例就是 Amazon Web Services(AWS)。框架则是应用程序(或其一部分)部分完成的“骨架”,团队在它的基础上添加自身特定的业务逻辑,例如 Java Spring UI 框架。大语言模型(LLM)也可被视为一种平台,团队通过提示词(prompt)对其进行扩展。平台和框架通过提供大量现成的能力,简化了应用程序的开发。

 

但有时候,团队需要的功能与平台或框架所提供的有所差异,举例来说,LLM 可能无法处理团队所需输入类型,比如,需要处理电话通话的音频并响应指令。LLM 在录音室环境下表现良好,但面对在嘈杂的机场录制的语音时,可能就无法工作。团队需要先构建音频降噪过滤器,但随后可能发现这些过滤器仍不足以解决问题。此时,他们就不得不训练自己的 LLM,以便使用包含“噪声”的对话数据。

 

用“铺好的路”作比喻,LLM 提供了一条已被验证的路径,但它无法带领团队抵达真正想去的地方。一旦发生这种情况,团队别无选择,只能在如下三种方案中做出选择:尝试扩展该平台(如果可行)、寻找另一个平台,或者从头构建自己的平台。

 

他们的挑战在于,需要花费一定的时间才能判断,究竟是基于现有平台继续开发更高效,还是必须为自己的场景构建独特的方案。他们的选择受限于平台开发者所做的决策。如果能接受这些决策,那么基于平台开发可能是最佳选择;但如果不能接受,那么在此基础上开发就是在浪费时间,而时间,恰恰是他们最宝贵的资产。

帮助厘清替代方案的三个关键问题

MVP 和 MVA 本质上是对潜在解决方案的“下注”。它们可能正确,也可能错误,而评估这些“赌注”是否成功的唯一方式就是实验。以下三个关于MVP的核心问题,有助于判断平台是否满足你的需求:这个产品值得构建吗?它能否在预期负载下实现可扩展性和性能? 它是否具备长期可维护性?

图 1:帮助确定架构决策的三个问题

 

团队在评估某个平台时,应结合这三个问题进行思考:

  1. 该平台有助于 MVP 的开发,还是阻碍 MVP 的开发?平台可能提供面向用户的功能,简化 MVP 的开发,但也可能附带团队无法接受的架构决策。借用道路的隐喻来说,唯一的方法是先沿着这条路走一小段,通过架构实验,检验平台所做的决策是否契合团队对 MVA 的需求。

  2. 该平台能否在预期的负载下实现可扩展性和性能?这里的难点在于,通常只有通过实验,你才能真正了解自己的可扩展性需求。借用道路的隐喻来说,你往往并不清楚自己需要的是一条车流稀少的双车道乡间小路,还是一条能够承载海量车流的高速公路。

  3. 基于该平台构建的架构是否具备长期可维护性?平台的演进速度通常比具体的业务系统更慢,因为它们的变更往往需要社区共识。当平台无法快速调整以满足需求时,团队就需要有明确的机制来扩展平台,直到平台本身能够做出相应修改。

 

这些问题不应该仅仅停留在激发思考和讨论的层面,必须通过实验进行实证评估。在实践中,这些实验体现为可执行的测试,可以在系统构建过程中持续运行。频繁对系统进行测试,以评估当前架构是否仍然适合目标用途,有助于避免后期出现不可控的大规模返工。

 

尽管上述三个问题看似按线性顺序展开,但实际上它们构成了一个循环(如图 1 所示):针对性能/可扩展性和模块化所做的调整,不应危及整体解决方案的有效性。

结论

平台就像一条现成的道路,可以让团队在交付 MVP 的旅程中更加轻松,但前提是,这条路确实通往他们想去的地方。团队在使用平台时面临的核心挑战在于,至少在项目初期,他们并不完全清楚自己的目的地,因此也无法确定平台所提供的“铺好的路”是否能带领他们抵达那里。

 

判断这条道路是否适合其 MVP 的一个重要方法,就是先走一小段,看看方向是否仍然正确,而这个“正确的方向”,正是由团队的质量属性需求(QAR)所定义的。

 

最终,团队不可避免地会在某个时刻离开平台所提供的“铺好的路”,走出自己的路径。通过实验,他们可以判断何时、何地需要这样做,是扩展平台以满足自身需求,还是开发平台完全未提供的全新解决方案,甚至彻底替换掉原有的平台。

 

The Architect’s Dilemma: Choose a Proven Path or Pave Your Own Way?

这两天技术圈里热议的一件事就是Amazon的流媒体平台Prime Video在2023年3月22日发布了一篇技术博客《规模化Prime Video的音视频监控服务,成本降低90%》,副标题:“从分布式微服务架构到单体应用程序的转变有助于实现更高的规模、弹性和降低成本”,有人把这篇文章在五一期间转到了reddithacker news 上,在Reddit上热议。这种话题与业内推崇的微服务架构形成了鲜明的对比。从“微服务架构”转“单体架构”,还是Amazon干的,这个话题足够劲爆。然后DHH在刚喷完Typescript后继续发文《即便是亚马逊也无法理解Servless或微服务》,继续抨击微服务架构,于是,瞬间引爆技术圈,登上技术圈热搜。

今天上午有好几个朋友在微信里转了三篇文章给我,如下所示:

看看这些标题就知道这些文章要的是流量而不是好好写篇文章。看到第二篇,你还真当 Prime Video 就是 Amazon 的全部么?然后,再看看这些文章后面的跟风评论,我觉得有 80%的人只看标题,而且是连原文都不看的。所以,我想我得写篇文章了……

原文解读

要认清这个问题首先是要认认真真读一读原文,Amazon Prime Video 技术团队的这篇文章并不难读,也没有太多的技术细节,但核心意思如下:

1)这个系统是一个监控系统,用于监控数据千条用户的点播视频流。主要是监控整个视频流运作的质量和效果(比如:视频损坏或是音频不同步等问题),这个监控主要是处理视频帧,所以,他们有一个微服务主要是用来把视频拆分成帧,并临时存在 S3 上,就是下图中的 Media Conversion 服务。

2)为了快速搭建系统,Prime Video团队使用了Serverless 架构,也就是著名的 AWS Lambda 和 AWS Step Functions。前置 Lambda 用来做用户请求的网关,Step Function 用来做监控(探测器),有问题后,就发 SNS 上,Step Function 从 S3 获取 Media Conversion 的数据,然后把运行结果再汇总给一个后置的 Lambda ,并存在 S3 上。

整个架构看上去非常简单 ,一点也不复杂,而且使用了 Serverless 的架构,一点服务器的影子都看不见。实话实说,这样的开发不香吗?我觉得很香啊,方便快捷,完全不理那些无聊的基础设施,直接把代码转成服务,然后用 AWS 的 Lamda + Step Function + SNS + S3 分分钟就搭出一个有模有样的监控系统了,哪里不好了?!

但是他们遇到了一个比较大的问题,就是 AWS Step Function 的伸缩问题,从文章中我看到了两个问题(注意前方高能):

  1. 需要很多很多的并发的 AWS Step Function ,于是达到了帐户的 hard limit。
  2. AWS Step Function 按状态转换收费,所以,贵得受不了了。

注意,这里有两个关键点:1)帐户对 Step Function 有限制,2)Step Function 太贵了用不起

然后,Prime Video 的团队开始解决问题,下面是解决的手段:

1) 把 Media Conversion  和 Step Function 全部写在一个程序里,Media Conversion 跟 Step Function 里的东西通过内存通信,不再走S3了。结果汇总到一个线程中,然后写到 S3.

2)把上面这个单体架构进行分布式部署,还是用之前的 AWS Lambda 来做入门调度。

EC2 的水平扩展没有限制,而且你想买多少 CPU/MEM 的机器由你说了算,而这些视频转码,监控分析的功能感觉就不复杂,本来就应该写在一起,这么做不更香吗?当然更香,比前面的 Serverless 的确更香,因为如下的几个原因:

  1. 不再受 Step Function 的限制了,技术在自己手里,有更大的自由度。
  2. 没有昂贵的 Step Function 云成本的确变得更低了,如果你把 Lambda 换成 Nginx 或 Spring Gateway 或是我司的 Easegress,你把 S3 换成 MinIO,你把 SNS 换成 Kafka,你的成本 还能再低。

独立思考

好了,原文解读完了,你有自己的独立思考了吗?下面是我的独立思考,供你参考:

1)AWS 的 Serverless 也好, 微服务也好,单体也好,在合适的场景也都很香。这就跟汽车一样,跑车,货车,越野车各有各的场景,你用跑车拉货,还是用货车泡妞都不是一个很好的决定。

2)这篇文章中的这个例子中的业务太过简单了,本来就是一两个服务就可以干完的事。就是一个转码加分析的事,要分开的话,就两个微服务就好了(一个转码一个分析),做成流式的。如果不想分,合在一起也没问题了,这个粒度是微服务没毛病。微服务的划分有好些原则,我这里只罗列几个比较重要的原则:

  • 边界上下文。微服务的粒度不能大于领域驱动里的 Bounded Context(具体是什么 大家自行 Google),也就是一个业务域。
  • 单一职责,高内聚,低耦合。把因为相同原因变化的合在一起(内聚),把不同原因变化的分开(解耦)
  • 事务和一致性。对于两个重度依赖的功能,需要完成一个事务和要保证强一致性的,最好不要拆开,要放在一起。
  • 跟组织架构匹配。把同一个团队的东西放在一起,不同团队的分开。

3)Prime Video 遇到的问题不是技术问题,而是 AWS  Step Function 处理能力不足,而且收费还很贵的问题。这个是 AWS 的产品问题,不是技术问题。或者说,这个是Prime Video滥用了Step Function的问题(本来这种大量的数据分析处理就不适合Step Function)。所以,大家不要用一个产品问题来得到微服务架构有问题的结论,这个没有因果关系。试问,如果 Step Funciton 可以无限扩展,性能也很好,而且白菜价,那么 Prime Video 团队还会有动力改成单体吗?他们不会反过来吹爆 Serverless 吗?

4)Prime Video 跟 AWS 是两个独立核算的公司,就像 Amazon 的电商和 AWS 一样,也是两个公司。Amazon 的电商和 AWS 对服务化或是微服务架构的理解和运维,我个人认为这个世界上再也找不到另外一家公司了,包括 Google 或 Microsoft。你有空可以看看本站以前的这篇文章《Steve Yegg对Amazon和Google平台的吐槽》你会了解的更多。

5)Prime Video 这个案例本质上是“下云”,下了 AWS Serverless 的云。云上的成本就是高,一个是费用问题,另一个是被锁定的问题。Prime Video 团队应该很庆幸这个监控系统并不复杂,重写起来也很快,所以,可以很快使用一个更传统的“服务化”+“云计算”的分布式架构,不然,就得像 DHH 那样咬牙下云——《Why We’re Leaving the Cloud》(他们的 SRE 的这篇博文 Our Cloud Spend in 2022说明了下云的困难和节约了多少成本)

后记

最后让我做个我自己的广告。我在过去几年的创业中,帮助了很多公司解决了这些 分布式,微服务,云原生以及云计算成本的问题,如果你也有类似问题。欢迎,跟我联系:[email protected]

另外,我们今年发布了一个平台 MegaEase Cloud,就是想让用户在不失去云计算体验的同时,通过自建高可用基础架构的方式来获得更低的成本(至少降 50%的云计算成本)。目前可以降低成本的方式:

  1. 基础软件:通过开源软件自建,
  2. 内容分发:MinIO + Cloudflare 的免费 CDN,
  3. 马上准备发布的直接与底层IDC合作的廉价GPU计算资源…

欢迎大家试用。

如何访问

注:这两个区完全独立,帐号不互通。因为网络的不可抗力,千万不要跨区使用。

产品演示

介绍文章

 

(全文完)

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

好烂啊有点差凑合看看还不错很精彩 (647 人打了分,平均分: 4.32 )

Loading...