Home
Softono
p

phodal

Professional software vendor delivering innovative solutions on the Softono platform. Specialized in both open-source and proprietary software development.

Total Products
12

Software by phodal

make
Open Source

make

:book::book::book::book::book: 写给软件工程师看的硬件编程指南

Education & Learning E-book Management Terminal & CLI Tools
217 Github Stars
understand-prompt
Open Source

understand-prompt

# 理解 Prompt:基于编程、绘画、写作的 AI 探索与总结 欢迎使用集成了这些模式的工具:https://github.com/prompt-engineering/click-prompt 目录: 1. [理解 Prompt](#%E5%9F%BA%E4%BA%8E%E7%BC%96%E7%A8%8B%E3%80%81%E7%BB%98%E7%94%BB%E3%80%81%E5%86%99%E4%BD%9C%E7%9A%84-ai-%E6%8E%A2%E7%B4%A2%E4%B8%8E%E6%80%BB%E7%BB%93%EF%BC%9A%E7%90%86%E8%A7%A3-prompt) 1. [图形生成:如何描述一张照片??](#%E5%9B%BE%E5%BD%A2%E7%94%9F%E6%88%90%EF%BC%9A%E5%A6%82%E4%BD%95%E6%8F%8F%E8%BF%B0%E4%B8%80%E5%BC%A0%E7%85%A7%E7%89%87%EF%BC%9F%EF%BC%9F) 1. [文本描述:第一个示例](#%E6%96%87%E6%9C%AC%E6%8F%8F%E8%BF%B0%EF%BC%9A%E7%AC%AC%E4%B8%80%E4%B8%AA%E7%A4%BA%E4%BE%8B) 2. [精准控图:结合 ControlNet](#%E7%B2%BE%E5%87%86%E6%8E%A7%E5%9B%BE%EF%BC%9A%E7%BB%93%E5%90%88-controlnet) 3. [更多的模型集:与二次元世界的照片(18 禁)](#%E6%9B%B4%E5%A4%9A%E7%9A%84%E6%A8%A1%E5%9E%8B%E9%9B%86%EF%BC%9A%E4%B8%8E%E4%BA%8C%E6%AC%A1%E5%85%83%E4%B8%96%E7%95%8C%E7%9A%84%E7%85%A7%E7%89%87%EF%BC%8818-%E7%A6%81%EF%BC%89) 4. [小结](#%E5%B0%8F%E7%BB%93) 2. [文章:如何围绕特定主题思考?](#%E6%96%87%E7%AB%A0%EF%BC%9A%E5%A6%82%E4%BD%95%E5%9B%B4%E7%BB%95%E7%89%B9%E5%AE%9A%E4%B8%BB%E9%A2%98%E6%80%9D%E8%80%83%EF%BC%9F) 1. [情境:如何用 ChatGPT 写一篇文章,以如何用 ChatGPT 写一篇文章?](#%E6%83%85%E5%A2%83%EF%BC%9A%E5%A6%82%E4%BD%95%E7%94%A8-chatgpt-%E5%86%99%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%EF%BC%8C%E4%BB%A5%E5%A6%82%E4%BD%95%E7%94%A8-chatgpt-%E5%86%99%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0?) 2. [构建更完整的上下文](#%E6%9E%84%E5%BB%BA%E6%9B%B4%E5%AE%8C%E6%95%B4%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87) 3. [构建框架(frameworks)](#%E6%9E%84%E5%BB%BA%E6%A1%86%E6%9E%B6%EF%BC%88frameworks%EF%BC%89) 4. [其它小建议:](#%E5%85%B6%E5%AE%83%E5%B0%8F%E5%BB%BA%E8%AE%AE%EF%BC%9A) 3. [代码生成:GitHub Copilot](#%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%EF%BC%9Agithub-copilot) 1. [完整示例:DDD + ChatGPT + Copilot](#%E5%AE%8C%E6%95%B4%E7%A4%BA%E4%BE%8B%EF%BC%9Addd-+-chatgpt-+-copilot) 2. [其它:代码生成开源模型](#%E5%85%B6%E5%AE%83%EF%BC%9A%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%BC%80%E6%BA%90%E6%A8%A1%E5%9E%8B) 4. [总结:AI 就像占卜](#%E6%80%BB%E7%BB%93%EF%BC%9Aai-%E5%B0%B1%E5%83%8F%E5%8D%A0%E5%8D%9C) 5. [其它:Macbook Pro 的吐槽](#%E5%85%B6%E5%AE%83%EF%BC%9Amacbook-pro-%E7%9A%84%E5%90%90%E6%A7%BD) 1. [AI 生态不友好](#ai-%E7%94%9F%E6%80%81%E4%B8%8D%E5%8F%8B%E5%A5%BD) 6. [本文相关资源](#%E6%9C%AC%E6%96%87%E7%9B%B8%E5%85%B3%E8%B5%84%E6%BA%90) 2. [Prompt 编程:打通低代码的最后一公里?](#%E5%AE%9E%E6%97%B6%E7%9A%84%E8%BD%AF%E4%BB%B6%E7%94%9F%E6%88%90-**%E2%80%94%E2%80%94-prompt-%E7%BC%96%E7%A8%8B%E6%89%93%E9%80%9A%E4%BD%8E%E4%BB%A3%E7%A0%81%E7%9A%84%E6%9C%80%E5%90%8E%E4%B8%80%E5%85%AC%E9%87%8C%EF%BC%9F**) 1. [引子](#%E5%BC%95%E5%AD%90) 2. [Prompt 编程:真正的无代码](#prompt-%E7%BC%96%E7%A8%8B%EF%BC%9A%E7%9C%9F%E6%AD%A3%E7%9A%84%E6%97%A0%E4%BB%A3%E7%A0%81) 1. [Prompt 编程特性 1:从需求到代码的直接转换](#prompt-%E7%BC%96%E7%A8%8B%E7%89%B9%E6%80%A7-1%EF%BC%9A%E4%BB%8E%E9%9C%80%E6%B1%82%E5%88%B0%E4%BB%A3%E7%A0%81%E7%9A%84%E7%9B%B4%E6%8E%A5%E8%BD%AC%E6%8D%A2) 2. [Prompt 编程特性 2:代码只是临时产物](#prompt-%E7%BC%96%E7%A8%8B%E7%89%B9%E6%80%A7-2%EF%BC%9A%E4%BB%A3%E7%A0%81%E5%8F%AA%E6%98%AF%E4%B8%B4%E6%97%B6%E4%BA%A7%E7%89%A9) 3. [Prompt 编程特性 3:次序化的分解框架](#prompt-%E7%BC%96%E7%A8%8B%E7%89%B9%E6%80%A7-3%EF%BC%9A%E6%AC%A1%E5%BA%8F%E5%8C%96%E7%9A%84%E5%88%86%E8%A7%A3%E6%A1%86%E6%9E%B6) 4. [Prompt 编程特性 4:语言即语言](#prompt-%E7%BC%96%E7%A8%8B%E7%89%B9%E6%80%A7-4%EF%BC%9A%E8%AF%AD%E8%A8%80%E5%8D%B3%E8%AF%AD%E8%A8%80) 3. [Prompt 编程 Demo:引导 ChatGPT 创建语言与建模](#prompt-%E7%BC%96%E7%A8%8B-demo%EF%BC%9A%E5%BC%95%E5%AF%BC-chatgpt-%E5%88%9B%E5%BB%BA%E8%AF%AD%E8%A8%80%E4%B8%8E%E5%BB%BA%E6%A8%A1) 1. [阶段一:让 ChatGPT 与你学 DDD](#%E9%98%B6%E6%AE%B5%E4%B8%80%EF%BC%9A%E8%AE%A9-chatgpt-%E4%B8%8E%E4%BD%A0%E5%AD%A6-ddd) 2. [阶段二:完整设计 + DSL 生成](#%E9%98%B6%E6%AE%B5%E4%BA%8C%EF%BC%9A%E5%AE%8C%E6%95%B4%E8%AE%BE%E8%AE%A1-+-dsl-%E7%94%9F%E6%88%90) 3. [阶段三:代码生成与低代码](#%E9%98%B6%E6%AE%B5%E4%B8%89%EF%BC%9A%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E4%B8%8E%E4%BD%8E%E4%BB%A3%E7%A0%81) 4. [小结](#%E5%B0%8F%E7%BB%93) 4. [实时的软件生成:自然语言即 Prompt,Prompt 即代码](#%E5%AE%9E%E6%97%B6%E7%9A%84%E8%BD%AF%E4%BB%B6%E7%94%9F%E6%88%90%EF%BC%9A%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%8D%B3-prompt%EF%BC%8Cprompt-%E5%8D%B3%E4%BB%A3%E7%A0%81) 1. [特征 1:自然语言即语言,语言即软件](#%E7%89%B9%E5%BE%81-1%EF%BC%9A%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%8D%B3%E8%AF%AD%E8%A8%80%EF%BC%8C%E8%AF%AD%E8%A8%80%E5%8D%B3%E8%BD%AF%E4%BB%B6) 1. [特征 2:生成式的软件架构](#%E7%89%B9%E5%BE%81-2%EF%BC%9A%E7%94%9F%E6%88%90%E5%BC%8F%E7%9A%84%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84) 1. [特征 3:自底向上生成](#%E7%89%B9%E5%BE%81-3%EF%BC%9A%E8%87%AA%E5%BA%95%E5%90%91%E4%B8%8A%E7%94%9F%E6%88%90) 5. [当前的挑战](#%E5%BD%93%E5%89%8D%E7%9A%84%E6%8C%91%E6%88%98) 6. [总结](#%E6%80%BB%E7%BB%93) 3. [如何利用好 AIGC ?从 AI 绘画的演进与 ChatGPT 现状出发](#%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8%E5%A5%BD-aigc-%EF%BC%9F%E4%BB%8E-ai-%E7%BB%98%E7%94%BB%E7%9A%84%E6%BC%94%E8%BF%9B%E4%B8%8E-chatgpt-%E7%8E%B0%E7%8A%B6%E5%87%BA%E5%8F%91) 1. [如何构建高质量的 AI 图形:精准控线 + 个人模型](#%E5%A6%82%E4%BD%95%E6%9E%84%E5%BB%BA%E9%AB%98%E8%B4%A8%E9%87%8F%E7%9A%84-ai-%E5%9B%BE%E5%BD%A2%EF%BC%9A%E7%B2%BE%E5%87%86%E6%8E%A7%E7%BA%BF-+-%E4%B8%AA%E4%BA%BA%E6%A8%A1%E5%9E%8B) 1. [严格化验收条件:Negative Prompt](#%E4%B8%A5%E6%A0%BC%E5%8C%96%E9%AA%8C%E6%94%B6%E6%9D%A1%E4%BB%B6%EF%BC%9Anegative-prompt) 2. [构架蓝图:ControlNet 精准控线](#%E6%9E%84%E6%9E%B6%E8%93%9D%E5%9B%BE%EF%BC%9Acontrolnet-%E7%B2%BE%E5%87%86%E6%8E%A7%E7%BA%BF) 3. [轻量小模型:DreamBooth 个人模型与风格化](#%E8%BD%BB%E9%87%8F%E5%B0%8F%E6%A8%A1%E5%9E%8B%EF%BC%9Adreambooth-%E4%B8%AA%E4%BA%BA%E6%A8%A1%E5%9E%8B%E4%B8%8E%E9%A3%8E%E6%A0%BC%E5%8C%96) 4. [完善与细化:局部绘制 —— InPaint 的手部修复](#%E5%AE%8C%E5%96%84%E4%B8%8E%E7%BB%86%E5%8C%96%EF%BC%9A%E5%B1%80%E9%83%A8%E7%BB%98%E5%88%B6-%E2%80%94%E2%80%94-inpaint-%E7%9A%84%E6%89%8B%E9%83%A8%E4%BF%AE%E5%A4%8D) 5. [小小的总结:严格化验收条件 + 构架蓝图 + 轻量小模型 + 完善与细化](#%E5%B0%8F%E5%B0%8F%E7%9A%84%E6%80%BB%E7%BB%93%EF%BC%9A%E4%B8%A5%E6%A0%BC%E5%8C%96%E9%AA%8C%E6%94%B6%E6%9D%A1%E4%BB%B6-+-%E6%9E%84%E6%9E%B6%E8%93%9D%E5%9B%BE-+-%E8%BD%BB%E9%87%8F%E5%B0%8F%E6%A8%A1%E5%9E%8B-+-%E5%AE%8C%E5%96%84%E4%B8%8E%E7%BB%86%E5%8C%96) 2. [个人 AI 策略:构架 + 磨炼 + 小模型](#%E4%B8%AA%E4%BA%BA-ai-%E7%AD%96%E7%95%A5%EF%BC%9A%E6%9E%84%E6%9E%B6-+-%E7%A3%A8%E7%82%BC-+-%E5%B0%8F%E6%A8%A1%E5%9E%8B) 1. [策略 0:拥抱变化](#%E7%AD%96%E7%95%A5-0%EF%BC%9A%E6%8B%A5%E6%8A%B1%E5%8F%98%E5%8C%96) 2. [策略 1:强化构架能力](#%E7%AD%96%E7%95%A5-1%EF%BC%9A%E5%BC%BA%E5%8C%96%E6%9E%84%E6%9E%B6%E8%83%BD%E5%8A%9B) 3. [策略 2:构建领域小模型](#%E7%AD%96%E7%95%A5-2%EF%BC%9A%E6%9E%84%E5%BB%BA%E9%A2%86%E5%9F%9F%E5%B0%8F%E6%A8%A1%E5%9E%8B) 3. [策略 3:探索与磨炼技巧](#%E7%AD%96%E7%95%A5-3%EF%BC%9A%E6%8E%A2%E7%B4%A2%E4%B8%8E%E7%A3%A8%E7%82%BC%E6%8A%80%E5%B7%A7) 4. [小结](#%E5%B0%8F%E7%BB%93) 文件目录: - Stable Diffusion 简易教程:[Stable Diffusion](./stable-diffusion) - 从 AI 绘画看 AIGC 的未来 - Jupyter 练习 AI 相关部分 - [translate.ipynb](./translate.ipynb) 中文翻译英文 - [codeai.ipynb](./codeai.ipynb) 代码自动生成 # 基于编程、绘画、写作的 AI 探索与总结:理解 Prompt PS:本文的图形部分因 “真实世界原因”,包含一些年龄受限的词汇,建议未成年人在监护人陪同下阅读。 就当前节点(2023.02.22)而言,我虽然研究过一段时间传统的机器学习,但是并不擅长深度学习等领域,所以很多 AI 领域相关的词汇,我是不擅长的,只为自己总结一下,方便在未来更新自己的认识。 内容主要是结合我过去擅长的编程、写作、绘画展开的: 1. 绘画:text 2 image。结合 Stable Diffusion 讲述一张图片的演化。 2. 写作:chat 2 article。结合 ChatGPT 讲述围绕特定主题的内容创作。 3. 编程:comment 2 code。结合 GitHub Copilot 如何加速日常的编程活动。 总体来说,**AI 相当于美国队长的实力,遇强则强,遇弱则弱**,关键其实在于 Prompt。所以,如何更好地完善 prompt,实现 prompt 工程就是:如何更好地使用 AI 的基础。 > Prompt 是一种文本输入方式,用于与自然语言处理模型(例如GPT)进行交互。Prompt 是一个短语或一句话,可以启动模型并产生与 Prompt 相关的输出。这个输出可以是一个单独的单词、一句话,或者是一段完整的段落。它可以控制模型的行为,使其产生特定的结果。 所以,如何更好地完善 prompt,实现 prompt 工程就是:如何更好地使用 AI 的基础。所以,在这篇文章里,我将介绍: 1. 用 text 2 image 来展示如何演进 prompt 2. 用 text 2 article 来展示充足 prompt 的优点 3. 结合 GitHub Copliot 来实现 prompt 完成工作 PS:感谢我老婆提供的照片。 ## 图形生成:如何描述一张照片?? 人类需要思考第一个问题:如何描述一张照片?在不关心感受的情况下,我们关注于主体、背景、颜色、构图等。 现在,我们脑海中出现一个场景: ![](./output/sample.jpg) 接着,用文本来抽象它。在这里,我结合一下 Stable Diffusion 来讲述一下过程。 ### 文本描述:第一个示例 生成 prompt: - phodal: 我想写一段话描述一张照片,帮我美化一下:一年轻女子穿着裙子,背对着镜头,在海边看着日落,太阳已经到地平线下面了,她的头发在风中飘动。 - ChatGPT: 这张照片中的场景令人心醉,一位年轻女子穿着一条裙子,站在海边,背对着镜头,凝视着地平线下的夕阳。太阳已经沉入了海面,一片金黄色的光芒在海面上弥漫,照亮了整片海洋,仿佛这个时刻被定格在了时间的某个角落里。女子的头发随着微风轻轻飘动,轮廓被余晖映照得柔和而温暖。她的目光凝视着远方,仿佛在思考着生命的意义和无尽的未来。这张照片中蕴含着一种深刻的内涵,让人感受到时间和自然的力量,也让人思考着自己在这个宏大而美丽的世界中的角色和意义。 如果转换成英语的话: > A young woman wearing a flowing dress stands at the edge of the sea, her back turned to the camera as she gazes upon > the glow of the setting sun. The ocean stretches out before her in all its grandeur, and the sun slowly sinks below > the horizon, casting a golden light over the water. The woman's hair sways gently in the breeze, her features softened > by the warm hues of the sunset. Her gaze is fixed on the distance, as if pondering the meaning of life and the endless > possibilities of the future. 重复了 N 次之后,你会得到你想要的成功的照片,当然也会有失败的出现(因为没有配置 negative prompt 过滤失败的情况): ![Failure](./output/failure-seeds.jpeg) 但是,熟悉各种搜索引擎的关键词的或者 NLP 的你,肯定知道,上面的大部分可能是废话,可以减化为如下的词,并添加一些专用的模型词汇,如`black hair`, `hand before body`, `no hand`, `bodycon dress`: > women back view without face, flowing dress, edge of the sea, backview, back turned to the camera, upon the glow of the setting sun, sun below the horizon, golden light over the water, hair sways gently, Chinese style clothes, black hair, 随后,添加一些反向 prompt,意思是不要这些内容,诸如于 AI 不擅长的:`bad hands`、`morbid` 等等。随后,不断调整 prompt,比如生成更好的 Prompt: <table> <tr> <td><img src="./output/women-backview-1.jpeg" width="256px" height="384px"></td> <td><img src="./output/women-backview-3.jpeg" width="256px" height="384px"></td> <td><img src="./output/women-backview-4.jpeg" width="256px" height="384px"></td> </tr> </table> 诸如于采用 [Magic Prompt](https://huggingface.co/spaces/Gustavosta/MagicPrompt-Stable-Diffusion)。 ### 精准控图:结合 ControlNet 由于生成的姿势是随机的、无法控制,所以我就引入了 ControlNet 插件 ——用来实现骨骼绑定、精准控线、线稿上色、依据深度图结构透视精准重绘等。现在,就能结合我们做的创作来完善生成的图形,诸如于我们绘制草图、或者输入一张原始图片,就可以生成我们预期的效果: 最后,生成的图片如下: ![Openpose](output/image-with-hed.png) 在我写完文章的时候,又修改了一下 prompt: > women back view without face standing on the sandy beach, bodycov full skirt, edge of the sea, back turned to the camera, upon the glow of the setting sun, black hair, sunset red to blue gradient sky 对应的 negative prompt 是: > (((simple background))),monochrome ,lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, lowres, bad anatomy, bad hands, text, error, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, ugly,pregnant,vore,duplicate,morbid,mut ilated,tran nsexual, hermaphrodite,long neck,mutated hands,poorly drawn hands,poorly drawn face,mutation,deformed,blurry,bad anatomy,bad proportions,malformed limbs,extra limbs,cloned face,disfigured,gross proportions, (((missing arms))),(((missing legs))), (((extra arms))),(((extra legs))),pubic hair, plump,bad legs,error legs,username,blurry,bad feet 效果凑合着,还是不错的。然后,我们可以做更多的尝试,配合一下参数调整(俗称炼丹): <table> <tr> <td><img src="./output/samples/01.jpeg" width="256px" height="384px"></td> <td><img src="./output/samples/02.jpeg" width="256px" height="384px"></td> <td><img src="./output/samples/04.jpeg" width="256px" height="384px"></td> </tr> <tr> <td><img src="./output/samples/05.jpeg" width="256px" height="384px"></td> <td><img src="./output/samples/06.jpeg" width="256px" height="384px"></td> <td><img src="./output/samples/08.jpeg" width="256px" height="384px"></td> </tr> </table> 也可以结合 inpaint 对失真的部分进行修复。 ### 更多的模型集:与二次元世界的照片(18 禁) 众所周知,AI 的生成质量是与模型息息相关的,所以好的质量需要有好的模型。 我们可以在 [https://civitai.com/](https://civitai.com/) 上找到更多的模型,不过因为年龄限制等原因,只建议你在安全的场所打开,不建议在公共场所打开。 真实的人物由于版权的种种原因,所以这个软件在二次元世界相当的流行。如下是常见的提示词来描述模型的质量,里面可能包含一些不适词语,为了体现真实世界,这里并不打算屏蔽。 提示词 : > modelshoot style, (wavy blue hair), ((half body portrait)), ((showing boobs, giant boobs, humongous breasts)), (( > beautiful light makeup female sorceress in majestic blue dress)), photo realistic game cg, 8k, epic, (blue diamond > necklace hyper intricate fine detail), symetrical features, joyful, majestic oil painting by Mikhail Vrubel, Atey > Ghailan, by Jeremy Mann, Greg Manchess, WLOP, Charlie Bowater, trending on ArtStation, trending on CGSociety, Intricate, > High Detail, Sharp focus, dramatic, photorealistic, black background, epic volumetric lighting, fine details, > illustration, (masterpiece, best quality, highres), standing in majestic castle 负面提示词 > (((simple background))),monochrome ,lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer > digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, > lowres, bad anatomy, bad hands, text, error, extra digit, fewer digits, cropped, worst quality, low quality, normal > quality, jpeg artifacts, signature, watermark, username, blurry, ugly,pregnant,vore,duplicate,morbid,mut ilated,tran > nsexual, hermaphrodite,long neck,mutated hands,poorly drawn hands,poorly drawn face,mutation,deformed,blurry,bad > anatomy,bad proportions,malformed limbs,extra limbs,cloned face,disfigured,gross proportions, (((missing arms))),((( > missing legs))), (((extra arms))),(((extra legs))),pubic hair, plump,bad legs,error legs,username,blurry,bad feet, > lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low > quality, normal quality, jpeg artifacts, signature, watermark, username, blurry 足够丰富的描述,可以帮助 AI 理解描述我们的需求: ![Better Quality](./output/better.jpeg) 上图为 AI 生成,也是同样场景下,不屏蔽的结果 —— 其原因是大部分的模型库加入了很多 18 禁的内容。 ### 小结 现在,让我们来思考一下,如何描述一张带人物的图片: - 详细的描述词。 - 人物的姿势。 - 丰富的模型。 - 反复修改的参数。 - 持续迭代。(基于 InPaint 等进行修改) 那么,文章呢? ## 文章:如何围绕特定主题思考? 结合一下先前 Stable Diffusion 的无数次的失败经验,我们应该先找好一个合适的框架来跑 ChatGPT。 在编写内容的内容,我们会有各种思路和原则:STAR、金字塔原理、5W1H等等。 我们可以以 STAR (Situation(情景)Task(任务)、Action(行动)和 Result(结果))可以作为与 ChatGPT 会话的基础: 1. 描述一个情境或背景,使读者对问题有更好的了解。 2. 说明任务或目标,告诉读者需要完成什么。 3. 描述你所采取的行动,例如使用什么方法、工具、策略等等。 4. 解释结果,包括遇到的挑战和取得的成果。 这样,对于我们展开思路会有一些帮助。 ### 情境:如何用 ChatGPT 写一篇文章,以如何用 ChatGPT 写一篇文章? phodal:我想写一篇文章,主题是《如何用 ChatGPT 写一篇文章,以如何用 ChatGPT 写一篇文章 》 示例: ![](./output/chatgpt-writing-article.png) 但是,这样的内容并非我们想要的。 而在这个标题里,其实 ChatGPT 已经理解了,如何写一篇文章,换作是一个冷门的话题,他就不知道了。在此时,我们需要构建一个更丰富的上下文: ![关注点驱动](images/focus-driven.png) 在这时,ChatGPT 给出的步骤依旧是错的,我们需要继续纠正。所以,我们可以考虑在一开始给一个完整的上下文: ### 构建更完整的上下文 对于 ChatGPT 来说,我们需要明确的 prompt:: ``` 我想写一篇文章,主题是《如何用 ChatGPT 写一篇文章,以如何用 ChatGPT 写一篇文章》,文章的大纲是: 1. 先用 100 字介绍一下 ChatGPT 2. 以 Julia 作为可以以具体的案例为例,介绍如何使用 ChatGPT 写一篇博客大赛的参赛作品。这包括如何确定主题、如何撰写开头和结尾、如何组织文章结构等等。主题是:《Julia Dataframe 在大数据分析领域的使用》,大纲中包含:用 200 字介绍一下 Julia 语言历史;介绍如何使用 DataFrames.jl,并有个代码示例;对比一下 Python Pandas、Apache Spark Dataframe;小结。 3. 对本文的内容进行总结,并再次强调使用 ChatGPT 进行写作的好处。 帮我写一篇文章? ``` 如此一来,我们提供的上下文就会更加准确,就可以看到一个范例: ![Demo](images/chatgpt-article-demo.png) 基于这个范例,可以思考我们的内容如何编写。 ### 构建框架(frameworks) ChatGPT 本身构建了一个框架,我们可以再输入一个有用的框架,以构建简单的反馈回路。于是,我开始教他 DDD,然后相互学习: ![DDD Copilot](images/ddd-copilot-1.png) 不过,ChatGPT 和上面的图片一样,经常丢失一些上下文。 phodal: 在这个 TicketOrder 聚合里,他应该包含哪些 API ? 在 TicketOrder 聚合里,可以包含以下一些 API: 1. createOrder():创建一个新的订单,可以接收顾客信息、场次信息、座位信息等参数,并返回一个唯一的订单号。 2. cancelOrder(orderId):取消某个订单,需要提供订单号。 3. getOrder(orderId):获取某个订单的详细信息,需要提供订单号。 4. reserveSeats(showTimeId, seatIds):在某个场次中预留座位,需要提供场次号和座位号。 5. confirmOrder(orderId):确认订单,此时座位被锁定并且订单状态被改为已确认。 6. payOrder(orderId, paymentInfo):支付订单,需要提供订单号和支付信息。 7. refundOrder(orderId):退票,需要提供订单号并且只能在订单状态为已确认时进行。 8. updateOrder(orderId, updateInfo):修改订单信息,可以更新顾客信息、场次信息、座位信息等,需要提供订单号和更新内容。 基本的 API 可以满足电影院订票业务的需求,但具体实现还需要根据业务场景和需求进行细化和调整。 ### 其它小建议: 相似的,与 Stable Diffusion 类似的是,AI 生成的内容经常出现各种问题,所以我们要注意: 1. 保持自然的表达。ChatGPT 能够生成流畅的语句,但有时可能会出现奇怪或不合适的表达。在使用 ChatGPT 进行写作时,需要注意保持自然的表达方式。 2. 适时修正错误。如果 ChatGPT 生成的内容不准确或有错误,应该及时进行修正,以保证文章的准确性和可读性。 3. 多次修改和调整。ChatGPT 的输出结果虽然会自动进行语法纠错和编辑,但是为了保证文章质量,还是需要经过多次修改和调整,不断完善文章结构和内容。 4. 保持简洁明了。在使用 ChatGPT 进行写作时,需要尽量保持简洁明了,避免使用过于复杂的句式和词汇,以便读者更好地理解文章内容。 5. 注重原创性和独特性。尽管 ChatGPT 能够生成大量的文章,但是为了在博客大赛中脱颖而出,还需要注重文章的原创性和独特性,尽可能地突出自己的风格和思想。 考虑到 ChatGPT 的代码并不那么准确,让我们使用 GitHub Copilot 生成更多的内容。 ## 代码生成:GitHub Copilot GitHub Copilot 是 GitHub 2021 年 8 月推出的一个 AI 代码生成工具,它可以根据你的代码,生成更多的代码。 ![Samples](images/github-copilot-1.png) 所以,对于 Markdown 本身来说,它也能提供一些帮助。当然了,在这方向它没有 ChatGPT 那么强大,经常会出现一些奇怪的重复代码。 但是,也并非不可用,我们可以使用它来生成一些代码片段,以便于我们进行修改。 ### 完整示例:DDD + ChatGPT + Copilot 如结合 Chat GPT 我们可以完成代码的前置设计(当然有些不靠谱): ![ChatGPT DDD](images/chatgpt-ddd.png) 复制生成的 API,写行注释告诉 Copilot:`convert to markdown table`,然后我们就有了: | API | 描述 | HTTP方法 | URL | 参数 | 返回值 | |--------|----------------|--------|-----------------------------------------------|--------|-------------| | 创建订单 | 创建一个新的订单 | POST | /ticket-orders | 创建订单信息 | 订单ID | | 查询订单 | 查询一个已存在的订单 | GET | /ticket-orders/{orderId} | 订单ID | 订单信息 | | 取消订单 | 取消一个已存在的订单 | DELETE | /ticket-orders/{orderId} | 订单ID | 取消成功或失败信息 | | 添加座位 | 向一个已存在的订单添加座位 | POST | /ticket-orders/{orderId}/seats | 座位信息 | 座位添加成功或失败信息 | | 移除座位 | 从一个已存在的订单中移除座位 | DELETE | /ticket-orders/{orderId}/seats/{seatId} | 座位ID | 座位移除成功或失败信息 | | 查询可用座位 | 查询某个场次电影的可用座位 | GET | /cinema-schedule/{scheduleId}/available-seats | 场次ID | 可用座位信息 | 接下来,我们可以使用 Spring + Copilot 来生成一些代码片段,以便于我们进行修改: ![github-copilot Sample](images/github-sample-2.png) 在有足够类相关信息的情况下,诸如于,我们提供一个注释: ```kotlin @PostMapping fun createTicketOrder(@RequestBody ticketOrder: TicketOrder): String { // check the schedule is valid, the seats are available, then create the order } ``` 它生成的代码会更加准确: ![GitHub sample 4](images/github-sample-4.png) 而如果我们添加了对应的 Exception 的处理注释:`check the schedule is valid, the seats are available, then create the order, if the schedule is not valid, throw NotScheduleAvailableException`,代码进一步转化为: ```kotlin @PostMapping fun createTicketOrder(@RequestBody ticketOrder: TicketOrder): String { // check the schedule is valid, the seats are available, then create the order, if the schedule is not valid, throw NotScheduleAvailableException // check the schedule is valid val schedule = cinemaScheduleService.getSchedule(ticketOrder.scheduleId) if (schedule == null) { throw NotScheduleAvailableException() } // check the seats are available val availableSeats = cinemaScheduleService.getAvailableSeats(ticketOrder.scheduleId) if (!availableSeats.containsAll(ticketOrder.seats)) { throw NotSeatAvailableException() } // create order return tickerOrderApplicationService.createOrder(ticketOrder) } ``` 在另外一些场景下,如果你的函数名和参数名都是英文,那么 Copilot 生成的代码也会更加准确: ![GitHub sample 5](images/github-sample-5.png) 而在单元测试的场景下,Copilot 更加强大和智能,它能够根据你的测试代码,生成更多的测试代码。 ### 其它:代码生成开源模型 我也尝试了下 Saleforce 的代码模型:[https://huggingface.co/Salesforce/codegen-6B-mono](https://huggingface.co/Salesforce/codegen-6B-mono),但是不能在我的 MBP 上跑上来,有些遗憾。 ## 总结:AI 就像占卜 你可能无法预期输出的是啥,总会有各种惊喜。 而其实,与 AI 的 prompt 相似的是:和人类聊天的时候,我们也需要提供足够的上下文。所以,你需要一下 ChatGPT,他并不总是能理解你的上下文。 ## 其它:Macbook Pro 的吐槽 公司配置的 Macbook Pro 2019 虽然有 AMD 显卡,但是没有 CUDA、ROCm 的支持,所以只能用 CPU 来跑了。 ### AI 生态不友好 诸如于 PyTorch,以前是支持 ROCm 加速的: ![ROCm](https://pytorch.org/assets/images/amd_rocm_blog.png) 现在的版本不行了:https://pytorch.org/get-started/locally/ 。 ## 本文相关资源 AI 生成图片: - Stable Diffusion Webui GitHub: https://github.com/AUTOMATIC1111/stable-diffusion-webui - 通用的 AI 模型社区:https://huggingface.co/ - Stable Diffusion AI 艺术模型社区:https://civitai.com/ (18 禁) 代码模型相关: - Salesforce 模型:https://huggingface.co/Salesforce/codegen-6B-mono - CarpserAI: https://huggingface.co/CarperAI/diff-codegen-6b-v2 ControlNet 加强: - 模型:https://huggingface.co/lllyasviel/ControlNet - 预编译:https://huggingface.co/kohya-ss/ControlNet-diff-modules # 实时的软件生成 **—— Prompt 编程打通低代码的最后一公里?** PS:这也是一篇畅想,虽然经过了一番试验,依旧有一些不足,但是大体上站得住脚。 传统的软件生成方式需要程序员编写大量的代码,然后进行测试、发布等一系列繁琐的流程。而实时生成技术则是借助人工智能技术,让计算机自动生成代码,并直接运行,从而大大提高了软件生成的效率和质量。 而实时生成技术是一种借助人工智能技术让计算机自动生成代码,并直接运行的方法,大大提高了软件生成的效率和质量。实现实时软件生成的一种方式是 Prompt 编程,可以将自然语言转化为直接运行的软件,而无需生成中间的一次性过渡代码。 ## 引子 从 2019 年,写了那篇《[**无代码编程](**[https://zhuanlan.zhihu.com/p/61288928](https://zhuanlan.zhihu.com/p/61288928))》开始,我也一直在思考,诸如于 [2021 年]([https://www.phodal.com/blog/frontend-strategy-2021/](https://www.phodal.com/blog/frontend-strategy-2021/)) 和 [2022 年]([https://www.phodal.com/blog/frontend-strategy-2023/](https://www.phodal.com/blog/frontend-strategy-2023/)) 的前端趋势总结。 先前,我并不看好当前的低代码/无代码方案,先我们来思考一下这个过程: 第一步,业务需求需要在人类脑海里转换一遍,转换为程序逻辑。 第二步,专业人员(如程序员)将这些逻辑转换到无代码系统中,生成应用并直接部署。 第三步,如果这个专业人员是程序员,那么新的需要变更时,他可能直接去修改源码。 而随着 ChatGPT 的进一步普及,你会发现你可以很容易将需求转化为**形式化格式**,进而转化而软件,从而使得软件开发的效率和速度大大提升。这种一次性代码的新型软件工程方法就是**实时软件生成**,便可以进入真正的 “**无代码**” 时代。 ## Prompt 编程:真正的无代码 - Prompt 编程是实现实时软件生成的一种方式,可以将自然语言转化为直接运行的软件,而无需生成中间的一次性过渡代码。每当需要运行程序时,只需要再运行一下 prompt 或者**触发词**,就可以直接运行起某个软件。 我们尝试从编写 Prompt 的经验里,归纳出一套适合于 Prompt 编程的逻辑,方便于大家理解。Prompt 编程有以下特性: 1)从需求到代码的直接转换; 2)代码只是临时产物; 3)次序化的分解框架。 这种一次性代码的新型软件工程方法可以进入真正的 “无代码” 时代。 ### Prompt 编程特性 1:从需求到代码的直接转换 用户只需要简单输入自己的需求就能够生成符合要求的软件,无需具备专业的编程技能。示例: ```yaml 我们来玩一个 mul 游戏,当我说 mul 开头,并输入数字时,你应该计算结果。示例:"""当我输入:mul 4,你应该输出:16""" ``` 在这个游戏里,我们使用: 1. **触发词**。mul 作为街头暗号,告诉程序你要按这个步骤走。类似于编程里的函数名。 2. 示例作为**输入和输出**。即预期输入某个结果时,输出我们想要的结果。 而在其实,已经是一个函数该有的样子:**输入、输出、关系**。值得注意的是,在简单的问题下,只要我们给定了示例,那么 AI 就会根据示例去计算输入和输出,不关注中间的处理逻辑。 接下来,再看一个隐含性编程的示例,依旧我们还是玩个游戏: ```yaml 我们能玩一个名为 kfc 的谜语创作游戏,当我说 "kfc",你写一个谜题,要求: 1. 不少于 200 字 2. 谜题不能出现肯德基和星期四 3. 正文可以夹杂小语种语言,如 """他说:Ciao""" 4. 谜底必须是 ###原来今天是肯德基疯狂星期四!!!### 格式类似于: """ ... """ ``` 这个例子已经相当的自由,我们也不关心中间的处理逻辑,只关注于结果。 ### Prompt 编程特性 2:代码只是临时产物 还记得在 [Prompt 编写模式]([https://github.com/phodal/prompt-patterns](https://github.com/phodal/prompt-patterns)) 中的 `wula` 游的例子吗?在 wula 游戏里,用户只需要输入问题,ChatGPT 便会自动给出代码、代码解释和文章。诸如于:`wula: 头共10,足共28,鸡兔各几只?`,那么 AI 便会开始自动开始二次编程: ```yaml 这是一个鸡兔同笼问题,题目给出了头的数量和足的数量,要求求出鸡和兔的数量。通常解决这个问题的方法是,假设鸡和兔的数量分别为 x 和 y,那么可以列出如下方程组: x + y = 10 2x + 4y = 28 解方程组可以得到 x=4,y=6,即鸡有4只,兔有6只。 鸡兔同笼问题的 JavaScript 代码实现 // 计算鸡兔数量的函数 function calcAnimals(heads, legs) { ..... ``` 在传统的体系之下,我们要关注于中间生成的 **JavaScript 代码**、**代码能不能运行**等等,所以我们在设计 wula 的时候,要求了: ```yaml 第二步. 代码编写:你需要用 JavaScript 编写解决这个问题的代码,并输出对应的代码,并介绍一下你的代码(不少于 200 字)。 第三步. 代码执行:你需要作为 JavaScript Console 执行第二步写的代码,如果没有给出测试数据,你需要自己随机生成测试数据,并将这些数据输入到代码中进行计算。 ``` 但是,回到第一个例子里,我们并不需要 JavaScript 代码,我们不需要任何代码。ChatGPT 可以为我们生成这部分的程序!!! 唯一限制我们的是: 1. ChatGPT 的上下文能力 2. 我们的 Prompt 编写能力 而在复杂的场景之下,Prompt 编写需要足够的形式化,才能让机器理解,也因此,我才把它称为 Prompt 编程。 ### Prompt 编程特性 3:次序化的分解框架 仅就当前而言 AI 的上下文能力,限制了 AI 解决复杂问题的能力。所以,它无法**一次性解决**复杂、中大型的问题,甚至于小型项目都存在一定的难度。同样的,对于人类来说,也存在这样的局限性,只是人类的问题是**领域知识有限**。 步骤详细见:[DDD Sample]([https://github.com/phodal/prompt-patterns/blob/master/ddd.sample.md](https://github.com/phodal/prompt-patterns/blob/master/ddd.sample.md)) 所以,作为编程核心的人类大脑,需要为机器设计一下工序,即每一步应该怎么做。所以,这个时候,就回到了 **DDD**(领域驱动设计)擅长的问题: ```yaml 我们来定义一下 DDD 游戏的步骤,一共有 6 个步骤,步骤如下: """ 第一步. 拆解场景。分析特定领域的所有商业活动,并将其拆解出每个场景。 第二步. 场景过程分析。选定一个场景,并使用 "{名词}已{动词}" 的形式描述过程中所有发生的事件,其中的名词是过程中的实体,其中的动词是实体相关的行为。 第三步. 针对场景建模。基于统一语言和拆解出的场景进行建模,以实现 DDD 设计与代码实现的双向绑定。 …… """ ``` 对于每一步,我们只要限定好我们的输出(Output),那么在给定输入的情况下,AI 就能给出我们想要的(也有可能跑偏了)结果: ```yaml ddd 第二步:场景过程分析。下面是每个场景的过程分析: 用户注册和登录 用户注册:用户填写注册信息,系统验证信息合法性,生成用户账户并保存。 用户登录:用户输入账户名和密码,系统验证账户密码,登录成功后跳转到用户中心。 商品展示和搜索 商品列表展示:系统从数据库中获取商品列表并展示,用户可以进行商品分类和排序。 商品搜索:用户输入关键词进行商品搜索,系统根据关键词从数据库中获取商品列表并展示。 …… ``` 在这时,我们就可以引导 AI 生成到 API 粒度: | API名称 | 请求方法 | 请求路径 | 请求体 | 响应体 | 描述 | | --- | --- | --- | --- | --- | --- | | 创建商品 | POST | /api/products | 商品信息 | 创建的商品信息 | 创建一个新的商品 | | 获取商品列表 | GET | /api/products | 无 | 商品列表信息 | 获取所有的商品列表 | | 获取单个商品信息 | GET | /api/products/{id} | 无 | 单个商品信息 | 根据商品 ID 获取单个商品信息 | | 更新商品信息 | PUT | /api/products/{id} | 更新的商品信息 | 更新后的商品信息 | 根据商品 ID 更新商品信息 | | 删除商品 | DELETE | /api/products/{id} | 无 | 无 | 根据商品 ID 删除商品信息 | 也可以生成代码,甚至于在上下文足够强大的时候,也是能够直接运行和自动优化的。 ### Prompt 编程特性 4:语言即语言 DSL(领域特定语言)是指专门针对某个特定领域的语言,通常具有严格定义的语法和规则,并且可以用来描述该领域中的问题、概念、过程和数据等。而经过形式化处理的自然语言可以被视为一种 DSL,因为它们具有明确定义的语法和规则,可以用来描述特定领域中的问题、概念、过程和数据等。 所以,简单来说如果我们限定了**输入和输出的格式**,那么我们的自然语言也是一种 DSL。这就意味着,限制我们进行次序化的关键就是形式化的抽象。 而,我们也可以通过编写 Prompt 来生成新的 DSL:`帮我设计一个 DSL,用来写函数` ```yaml 好的,以下是一个简单的DSL设计,可以用来写函数: 1. 函数定义 在这个段落中,定义一个函数,包括函数名、参数和函数体等。 ``` 函数: 函数名: 描述函数的名称 参数: 参数1: 描述参数1的名称和类型 参数2: 描述参数2的名称和类型 ... 函数体: 描述函数的实现细节 ``` 2.。。。 ``` 简单来说,我们可以通过 Prompt : 1. 借用 Prompt 设计自然语言式的 DSL 2. 将自然语言式的 DSL 融入 Prompt 3. 使用次序化的方式来实现 Prompt 自我编程 进而,我们就得到了一个可以自举的 Prompt。 ## Prompt 编程 Demo:引导 ChatGPT 创建语言与建模 说了,这么多我们来看一个结合我之前设计的 DSL 示例,即 [Fklang](https://github.com/feakin/fklang)。 ### 阶段一:让 ChatGPT 与你学 DDD 在 Fklang 中,我们采用下面的形式来表示 ContextMap: ```yaml ContextMap TicketBooking { Reservation -> Cinema; Reservation -> Movie; Reservation -> User; } ``` 但是,自己设计这样的 DSL 太累了,可以让 ChatGPT 来设计。在你开始之前,需要确认一下 ChatGPT 中的 DDD、DSL、事件风暴等的概念是否与你理解的一致。 第一个问题:`设计一个 DSL 来表示 DDD 里的 Context Map 。` ```yaml ContextMap: Subdomain { Name: [subdomain_name] } [CollaboratesWith: [other_subdomain_name], ...] [ConformistWith: [other_subdomain_name], ...] [Antagonizes: [other_subdomain_name], ...] ... ``` 第二个问题:用这个 ContextMap DSL 对在线电影订票系统建模。对于其它问题也是相似的。 第三个问题:设计一个 DSL 来描述事件风暴的 事件建模 吗? 第四个问题:能用 事件建模的 DSL 来表示用户购票的流程吗? 一旦,我们觉得设计跑歪了,就可以重新设计。如果下是生成的复杂的 DSL: ```yaml EventStorming: Domain { Name: "电影订票系统" } Event { Name: "用户提交订单" } Triggered By: "用户选择电影、场次、座位,确认订单" Description: "用户提交订单,包括所选电影、场次、座位等信息" Actors: ["用户"] Action: "将用户提交的订单信息保存到订单数据库中" Outcome: "订单状态被标记为已提交" ``` 第五个问题…… 剩下的你都会了,不会的,可以问 ChatGPT。 ### 阶段二:完整设计 + DSL 生成 第一个问题:能总结一下,我们这里用 DDD 设计 购票系统到落地代码的过程吗? 第二个问题:能为每个过程,设计一个合理的 DSL 吗,并展示他们? 展示一部分神奇的 DSL: ```yaml generate java-code from-domain-model target-package: com.example.movieticket.order source-model: order-domain-model service-mapper 订单服务映射 map-method: 查询电影 to-class: MovieRepository to-method: findBySchedule map-method: 查询座位 to-class: SeatRepository to-method: findByRowAndColumnAndStatus ``` 现在,有意思的地方来,有了上面的一系列 DSL 之后,我们就可以接入到代码系统中。 ### 阶段三:代码生成与低代码 只要 ChatGPT 上下文能力足够强壮,或者支持 LoRA 式的插件模式,我们就能实现从需要到现有的任何系统中。 第一个问题:为电影订票系统设计一个 DDD 风格 Java 工程的代码目录结构。 第二个问题:结合 Spring 设计一下购票流程的 RESTful API …… 有点懒,就先这样吧。后面的部分,就可以结合 GitHub Copilot 去实现了。 ### 小结 结合 Prompt 编程,低代码到了一定的成熟度,我们就可以发现更好玩的东西:实时的软件生成 ## 实时的软件生成:自然语言即 Prompt,Prompt 即代码 实时软件生成核心思想是,通过算法和机器学习来自动生成代码,让计算机根据用户需求,快速生成符合要求的软件。这种技术能够自动化完成代码的编写、测试、发布等流程,大大缩短软件开发周期,降低了开发成本,提高了开发效率。 当我们想构建这样一软件用于实时生成软件时,它需要具备以下的特征: ### 特征 1:自然语言即语言,语言即软件 即如上面的 Prompt 编程所述,可以通过设定层层转换,直接将需求直接转换为软件。 ### 特征 2:生成式的软件架构 软件本身不需要架构,架构是 AI 自动生成和调整的。 ### 特征 3:自底向上生成 现有的语言本身需要 REPL 环境、操作系统、编程语言、语言底层库、库等一系列软件,对于 AI 而言,他能学习这些通用能力,自操作系统底层一样,逐步往上构建出软件的运行环境,以及软件本身 。 ## 当前的挑战 对于当前而言,我们还存在些挑战: - 现行组织架构难以支撑内部 ChatGPT。如内部权限、架构等的管理 - 通用大模型无法满足。领域特定能力有限,需要构建 LoRA 以更好的支持。 - 细节能力实现较差。在编程实现上,远不如 GitHub Copilot 当然了,受限于个人能力,可能还有别的一些挑战。 ## 总结 本文介绍了 Prompt 编程的特点和实时软件生成的核心思想。Prompt 编程是一种次序化的分解框架,可以让机器根据用户需求自动生成代码。实时软件生成技术可以大大提高软件生成的效率和质量,同时也让软件开发变得更加简单。然而,现有组织架构难以支撑内部 ChatGPT,上下文能力有限,细节能力实现较差,这些都是实现 Prompt 编程和实时软件生成的难点。 # 如何利用好 AIGC ?从 AI 绘画的演进与 ChatGPT 现状出发 PS:就本文的结论而言,我相信你已经或多或少的有所体会了。也因此,本文更多的是展现一个思考的过程,而不是一个纯粹的结论。 > AIGC 是什么?它是指通过机器学习、自然语言处理等人工智能技术,让计算机自动生成文字、图像、音频、视频等各种类型的内容。它能够帮助企业和个人降低创作成本、提高生产效率、增强创意输出等。 开始之前,先说结论:哪怕仅就当前的 AIGC 成熟度,我们都明白:**人类应该去做更高价值的事**,也因此在当前的工作模式上呈现的是,三步区: - 蓝图设计(人类)。 负责创意性的思考与设计工作,如场景、软件架构等。 - 机械化生成(机器)。将创意借助工具或者人转换为 Prompt,然后交给 AIGC 生成。 - 细节修复(人类)。对于 AIGC 生成不合理、不适宜法律法规等的地方,进行修改。 因此,对于诸多**通用的大众领域**,人类这样的碳基生物而言,如果不能从思维框架来驾驭 AIGC。而对于细分领域来说,只要在足够卷的情况下,AIGC 也会给予我们更多的惊喜。 与 ChatGPT 的黑盒相比,类似于 Stable Diffusion 白盒开源,可以让我们更了解 Prompt 应该如何编写?如何更好地利用 AIGC。所以,本文的第一部分就是从现有的 AI 绘画的变化来看,如何更好的利用 AIGC。而第二部分则是结合 ChatGPT 的现状来看,如何更好的利用 AIGC。 ## 如何构建高质量的 AI 图形:精准控线 + 个人模型 注意:请在取得授权的情况下,进行个人模型的练习,避免侵犯个人肖像权。 我们的例子,依旧是基于 Stable Diffusion,开源模型与开源软件才是人类的未来。太长不看图: ![Stable Diffusion 概览](images/stable-diffusion-overview.png) 对于一个绘画过程来说,我们可以通过如下的方式,逐步引导绘图应用: 1. 编写详细的 Negative Prompt,以淘汰不合理的生成内容。 2. ControlNet 作为基准骨架,引导最终效果,过滤不合理的图像。 3. 训练与融合个人模型,以构建领域特定的用途。 总的来说:过滤不合理的图像,就能提升生成质量。我们就可以,设计出初步符合需求(在不看 AI 画出来的手情况下)的框架性方案。 ### 严格化验收条件:Negative Prompt > Negative Prompt 会将模型的目标从一般的高概率生成样本转换为生成与负向提示不匹配的低概率样本,从而迫使模型更加关注图像的细节和特征,提高其生成的图像的质量和逼真度。 我们的故事依旧可以从:`微笑的女孩探出火车窗外` 故事开开始,简单地翻译成英语,来作为我们的 prompt:`smiling girl leaning out the train window`。在只有 Prompt 的情况下,会生成各种奇怪的图形,所以我们需要添加 Negative Prompt。 所以在 Stable Diffusion 里,我们就可以通过它来提升质量: ![Negative](images/stable-diffusion-with-negative.jpeg) 而从结果来看,模型与我们想要的图,还存在一定的距离。对于 ChatGPT 也是类似的,所以我们需要相似的模式:诸如于 `写一个不超过 800 字的作文`,又或者是 `写一个作文,要求如下:1. 不超过 800 字`。 ### 构架蓝图:ControlNet 精准控线 > ControlNet 是一种神经网络结构,旨在通过添加额外条件来控制扩散模型。在特定场景下,ControlNet被用于生成类似建模效果(法线贴图)的中间图和相关的图像。这种技术可以被应用于多个领域,如骨骼绑定、精准控线、线稿上色、深度图结构透视精准重绘等。 简单来说,在人像领域,通过手绘特定的姿势、从照片中解析等方式,创建一个人物姿势,绘制出来的图便采用类似的格式。如下图所示: ![ControlNet](images/stable-diffusion-with-controlnet.jpeg) 从形状和生成的效果来说,除了脸部等细节不是特定令人满意之外,基本能满足使用的需求。而在更好地机器加持下,我们能得到更高分辨率,就可以靠人工修复脸部的问题。 而在写作场景之下,只要我们给了 ChatGPT 大纲,那么他就能帮助我们生成文章。唯一的问题是,我们不能添加上自己的写作风格、历史作品,否则我们可以更加容易使用这个作品。而在那之前,我们需要思考什么是我们的作品?什么是我们的风格? ### 轻量小模型:DreamBooth 个人模型与风格化 注意:请在取得授权的情况下,进行模型的练习与作品创作。除了 DreamBootb 还有其他工具可使用,但由于时间限制,我就没有展开进一步研究。 > 融合个人模型是指将训练后的个人风格和特点融入到 AI 绘画模型中,使其生成的画作更贴近个人风格和需求,提高生成画作的个性化和定制化。 诸如在 Stable Diffusion 中,我们可以用自己的头像结合 DreamBooth 等工具训练,以得到一个融合自己风格的模型。在二次元世界里,最常被使用的是 "个人头像",以用于生成动漫或者 idol。效果如下: ![DreamBooth](images/stable-diffusion-with-controlnet-dreambooth.jpeg) PS:在取得某人同意的情况下,放一张动画化的结果(当然了,取的是不像本人的照片): ![Final](images/stable-diffusion-hug8217.jpeg) 在 Stable Diffusion 的模型尝试之后,我们可以发现:云 GPU + 模型可插件化 + 算力要求逐步下降之后,会使得个人的小模型会变成越来越普及,所以我训练的模型也只在云上跑了几分钟。 ### 完善与细化:局部绘制 —— InPaint 的手部修复 众所周知,当前的 AI 绘图还存在诸多细节问题,比如手、脚等,因此需要一定的人类修复画师。又有一部分人自此成为了服务于 AI 的打工人。如下是使用 InPaint 修复手部时生产出来的,人类画师就需要从中挑选出合适的照片: ![](images/try-fix-hands.jpeg) 最后,总算,先找到一张可以凑合着交差的: ![](images/hand-final-result.jpeg) 除此,还可以选择对图形进行裁剪,或者使用 Photoshop 等工具进行重绘等。 ### 小小的总结:严格化验收条件 + 构架蓝图 + 轻量小模型 + 完善与细化 要想清晰的表达自己的需求,我们需要: - **严格化的验收条件**,即通过 Prompt 描写需求,通过 Negative Prompt 排除异常 - **表达构架蓝图**,即通过 ControlNet 创建所需要的内容骨架,控制 - **轻量的领域小模型**,即通过 DreamBooth,结合灵活的架构模型,来丰富 AI 模型。 - **完善与细化**,即通过 InPaint 对有缺陷的部分进行修复,如局部绘制。 当然了,在 Stable Diffusion 里,还可以通过 Inpaint 等方式进行修复。 ## 个人 AI 策略:构架 + 磨炼 + 小模型 在先前的两篇文章里,我们已经不断地在探索适合于个人的 AI 策略: - 《[基于编程、绘画、写作的 AI 探索与总结](https://github.com/phodal/ai-research/)》 - 《[Prompt 编写模式:如何将思维框架赋予机器](https://github.com/phodal/prompt-patterns)》。 对于我来说,我的 AI 策略大致是: - 强化构架能力。强化架构设计、软件设计、抽象设计等。 - 构建小模型。在未来合适的时候,诸如于合适的开源 GPT 等。 - 探索与磨炼技巧。探索更多的 AI 解决方案,如 Notion AI 等;持续探索 Prompt 模式等。 对于修复与完善来说,由于 AI 本身是无法达到这么精细的,所以我的想法是**持续构建小工具**。 ### 策略 0:拥抱变化 首先,我们要理解 AIGC 真的带来变化,尽管现今的 AI 并不能完整的代替我们,但是已经能大大提升效率。 作为一个知名的 “开源挖抗” 作者,在我使用 GitHub Copilot 的初期,觉得这 TM(Trademark) 就是一个智障。而我适应了:**如何与智障沟通**之后,我悟了,我才是 ”智障“ —— 只有理解机器的 API 与工作方式,才能利用好机器。 ### 策略 1:强化构架能力 AI 工具无法替代个人的感性思考和直觉,所以个人在设计过程是非常重要性的。 强化设计是指通过 AI 技术释放个人的创造力,帮助个人在设计中实现更高效、更优质的创造成果。这个策略的核心在于使用 AIGC 工具来自动生成大量的创意元素,例如图像、文字、音频等,从而将创造的效率提高到一个新的水平。 在实践中,个人可以通过以下方式强化自己的构架能力: - 持续学习新的设计理念和创意方法,不断拓展自己的知识面和视野。例如,了解一些新兴的设计趋势,学习如何将传统的设计元素与现代的技术手段相结合,从而创造出更有创意和张力的作品。 - 坚持思考和探索,不断挑战自己的思维方式和想象力。通过不断思考和实践,将自己的思维方式和想象力逐渐转化为可操作的设计构架,从而在 AIGC 工具的帮助下实现更高效的创造。 除此,我们还应该熟练掌握使用 AIGC 工具的方法,尤其是一些高级的特性。例如,对于文本生成任务,可以使用 Negative Prompt 等技巧提高生成的质量;对于图像生成任务,则可以使用 ControlNet 等技术实现更精准的控制。 ### 策略 2:构建领域小模型 PS:此处需要持续寻找合适的工具,就当前而言,只有 AI 绘图领域是**相对比较成熟**(可用)的。 每个人的知识面是不同的,知识体系也是不同的。因此,我们不能期望一个通用的大模型能够满足所有人的需求。相反,我们应该尝试构建适合自己领域的小模型。通过选择合适的数据集、算法和网络结构,我们可以快速训练出一个专门用于解决自己问题的小型模型。这个模型不需要太复杂,只需要满足自己的需求即可。这样可以提高模型的效率和准确度,并且减少训练时间和计算资源的消耗。 例如,对于一个博客作者来说,可以使用 GPT-3 来帮助自己快速生成博客文章的开头或结尾段落,也可以通过训练自己的小模型,生成符合自己风格的文章内容。对于一名摄影师来说,可以通过构建小模型来辅助自己完成相册的排版、图像剪辑等工作。 所以,对于而言,我有 900+ 的博客,从中训练出来的写作风格,大概是能像我的 —— 也存在不同时机的风格不一样的问题。 ### 策略 3:探索与磨炼技巧 对于探索而言,也是最近才有时间和精力去探索,加入了公司的相关讨论群后,也获得了更多的输入。只是对于我来说,更多的是想把 AI 融入到日常事务中,以提升工作效率,所以也不想去创建微信群。 对于技巧来说,其实更多的是要去理解 AI 是如何 work 的,并将这种模式整合到自己的思维方式里。 除此,我们还可以思考如何将思维框架赋予 AI,以完成更闭环的工作。诸如于 GitHub Copilot 可以帮我们写代码,但是无法从宏观上理解业务问题、整体性的架构问题,生成的代码只是从局部考虑的。因此,我们需要通过不断地磨炼自己的技能和能力,来提高自己的综合素质和创造力。如我正在持续丰富的 [phodal/prompt-patterns](https://github.com/phodal/prompt-patterns) 也算是我的磨炼技巧。 ## 小结 最后,再让 AI 总结一下四个策略: 1. 拥抱变化,尽管人工智能并不能完全代替人类,但它已经能够大大提高效率。 2. 强化构架能力,因为人工智能工具无法代替个人的感性思考和直觉。 3. 构建领域小模型,可以快速训练出一个专门用于解决自己问题的小型模型。 4. 探索与磨炼技巧,探索 AI 能力并持续构建小工具,来修复和完善自己的 AI 增强系统。 结论,AI 在短期内还是智障,但是已经可以大大提升效率了。

Education & Learning AI Tools
5.4K Github Stars
prompt-patterns
Open Source

prompt-patterns

# Prompt 编写模式:如何将思维框架赋予机器 欢迎使用集成了这些模式的工具:https://github.com/prompt-engineering/click-prompt 本书旨在介绍一系列的 Prompt 编写模式,以更好地应用 Prompt 对 AI 进行编程。 我们非常感谢您对本书的关注和支持,并欢迎您为该项目做出贡献!您可以通过以下方式参与本书的开发: - 发现问题并报告:如果您在使用本书时发现任何问题或错误,请在项目的 Issue 页面中提出问题,我们将尽快修复。 - 编写和改进章节:如果您想要贡献新的章节或者改进现有的章节,欢迎您提交 Pull Request。 - 翻译本书:如果您是非英语母语的人士,并且想要将本书翻译成其他语言,请在项目的 Issue 页面中提出请求,我们将会指导您如何进行翻译。 - 分享本书:如果您认为本书对其他人也有帮助,请将本书分享给您的朋友和同事,让更多的人了解和使用 Prompt 进行 AI 编程。 我们希望通过大家的共同努力,能够让本书更加完善和实用,为更多的开发人员提供有价值的帮助和指导。谢谢您的支持和贡献! ChatGPT Simple Cheatsheet ![](cheatsheet/prompt-simple-cheatsheet.jpg) Download: [pdf](cheatsheet/prompt-simple-cheatsheet.pdf), [pptx](cheatsheet/prompt-simple-cheatsheet.pptx) 如何理解 Prompt ? ![Prompt Enginneering](patterns/prompt-engine.svg) > prompt 通常指的是一个输入的文本段落或短语,作为生成模型输出的起点或引导。prompt 可以是一个问题、一段文字描述、一段对话或任何形式的文本输入,模型会基于 prompt 所提供的上下文和语义信息,生成相应的输出文本。 举个例子,对于一个语言模型,prompt 可以是 "The cat sat on the",模型可以通过对接下来的词语进行预测,生成类似于 "mat"、"chair"、"sofa" 等不同的输出: ![The Cat](images/the-cast-sit-on.png) 上图为 Stable Diffusion 生成 (Prompt: The cat sat on the , Steps: 30, Sampler: Euler a, CFG scale: 7, Seed: 234310862, Size: 512x512, Model hash: d8722b4a4d, Model: neverendingDreamNED_bakedVae) Prompt 在人工智能语言生成领域中扮演着重要的角色,因为它可以帮助模型更好地理解用户意图,并生成更准确、有意义的文本内容。 诸如于如下的 prompt > women back view without face, flowing dress, edge of the sea, backview, back turned to the camera, upon the glow of the setting sun, sun below the horizon, golden light over the water, hair sways gently, Chinese style clothes, black hair, 可以在 Stable Diffusion 生成图片(配置了 negative prompt): <table> <tr> <td><img src="./images/backview-new-1.png" width="256px" height="256px"></td> <td><img src="./images/backview-new-2.png" width="256px" height="256px"></td> <td><img src="./images/backview-new-3.png" width="256px" height="256px"></td> </tr> </table> 所以,质量看上去不错,但是可能不是你想要的。在 ChatGPT 则可以生成文本,质量上也是相似的,但是对于 AI 输出的文本来说,质量并没有这么直观。 # 模式要素 > 省去几千字 如果您指的是 AI 领域中的 prompt 模式,它通常是指一种输入-输出的数据格式,用于训练和评估机器学习模型。下面是一个完整的定义: - 模式名称(Pattern Name):Prompt 模式 - 问题描述(Problem):如何准备训练数据,以便用于机器学习模型的训练和评估。 - 解决方案(Solution):Prompt 模式是一种输入-输出数据格式,它由一个输入文本和一个输出文本组成。输入文本是一个问题或指令,输出文本是模型预测的答案或结果。通过使用这种格式,可以减少训练数据的需求量,提高模型的泛化性能,同时也使得模型的输出更易于理解和解释。 - 效果(Consequences):使用 Prompt 模式可以简化训练数据的准备过程,提高模型的效率和准确率,同时也增加了模型的可解释性和可理解性。 - 适用性(Applicability):Prompt 模式适用于自然语言处理领域中的各种任务,如文本分类、情感分析、问答系统、机器翻译等。它也可以用于其他领域中需要使用自然语言作为输入和输出的任务。 - 结构图(Structure):Prompt 模式的结构由一个输入文本和一个输出文本组成,它们被定义为模型的输入和输出。通常,输入文本包括一些关键词或短语,用于指定模型需要执行的任务或操作,而输出文本则是模型的预测结果。 - 参考(References):相关的文献包括 "GPT-3: Language Models are Few-Shot Learners", "Zero-Shot Learning - A Comprehensive Evaluation of the Good, the Bad and the Ugly" 等。常用的机器学习框架包括 TensorFlow,PyTorch 等。 # 核心思想:概念与类比 开始之前,可以看一下这个问题示例: 1. 设计模式的要素是哪些? 2. 对于 AI 领域的 prompt 编写来说,我们通常使用的模式有哪些? 3. 能将 AI 领域的 prompt 常见的设计模式用 "设计模式要素" 的格式一一表达吗? 核心思想,将设计模式要素作为一个概念,让 AI 类比到 prompt 里的模式。详细见: 1. [design-pattern.analogy](./design-pattern.analogy.md) 2. [design-pattern.analogy2](./design-pattern.analogy2.md) 当然了,类比和定义概念不一定都会成功。 # 基础模式 四种基础模式: - 特定指令(By specific):在这种模式下,我们给模型提供一些特定信息,例如问题或关键词,模型需要生成与这些信息相关的文本。这种模式通常用于生成答案、解释或推荐等。特定信息可以是单个问题或多个关键词,具体取决于任务的要求。 - 指令模板(Instruction Template):在这种模式下,我们给模型提供一些明确的指令,模型需要根据这些指令生成文本。这种模式通常用于生成类似于技术说明书、操作手册等需要明确指令的文本。指令可以是单个句子或多个段落,具体取决于任务的要求。 - 代理模式(By proxy):在这种模式下,可以充当了一个代理,代表某个实体(例如人、角色、机器人等)进行操作或交互。代理模式的核心思想是引入一个中介对象来控制对实际对象的访问,从而实现一定程度上的隔离和保护。诸如于在 ChatGPT 中,"act as xxx" 可以让 ChatGPT 充当一个代理,扮演某个角色或实体的身份,以此来处理与该角色或实体相关的任务或请求。 - 示例模式(By demonstration):在这种模式下,我们给模型提供一些示例文本,模型需要生成与示例文本类似的文本。这种模式通常用于生成类似于给定示例的文本,例如自动生成电子邮件、产品描述、新闻报道等。示例文本可以是单个句子或多个段落,具体取决于任务的要求。 ## 特定指令(By specific) ![Prompt Enginneering](patterns/specific.svg) > 在这种模式下,我们给模型提供一些特定信息,例如问题或关键词,模型需要生成与这些信息相关的文本。这种模式通常用于生成答案、解释或推荐等。特定信息可以是单个问题或多个关键词,具体取决于任务的要求。 如 `翻译`、`告诉我`,以我们的开头来说: - 定义一下 prompt 工程 类似的场景还可以有: - 翻译一下:永和九年,岁在癸丑,暮春之初,会于会稽山阴之兰亭,修禊事也。 - 转为现代汉语:永和九年,岁在癸丑,暮春之初,会于会稽山阴之兰亭,修禊事也。 对应的,还有一系列的子模式 ### 子模式 如我们通过下面的 prompt 转换了 ChatGPT 输出的子模式: > 转化为 markdown 的 """```markdown {} ```""" 表格形式,其中的字段为英语模式、中文、简述、示例。 表格示例: | 英语模式 | 中文 | 简述 | 示例 | |----------------------|-----|-----------------------|-----------------------------------------------------| | Completion-based | 补全型 | 用户提供部分文本,AI 根据上下文生成建议 | 用户输入“我想买一件…”,ChatGPT 生成“红色连衣裙” | | Classification-based | 分类型 | 用户提供问题或任务描述,AI 生成答案 | 用户输入“如何做巧克力蛋糕?”ChatGPT 生成“将巧克力蛋糕放入预热好的烤箱中烤25-30分钟。” | | Generation-based | 生成型 | 用户提供初始信息,AI 生成新文本 | 用户输入“科技创新”,ChatGPT 生成“人工智能是科技创新领域的重要方向之一。” | | Translation-based | 翻译型 | 用户提供文本,AI 进行翻译 | 用户输入“Hello”,ChatGPT 生成“你好” | | Question-answering | 问答型 | 用户提供问题,AI 生成答案 | 用户输入“什么是机器学习?”,ChatGPT 生成“机器学习是一种人工智能的分支。” | | Conversation-based | 对话型 | 用户与 AI 进行对话,AI 生成回答 | 用户输入“今天天气怎么样?”ChatGPT 生成“今天天气晴朗,温度适宜。” | | Summarization-based | 摘要型 | 用户提供文本,AI 生成文本摘要 | 用户输入一篇文章,ChatGPT 生成该文章的摘要 | 其重点是:Output Driven 在提示的开头包含所有相关信息,包括任何上下文或期望的结果,以及 长度,格式样式等。诸如于:`用 100 字总结一下 ChatGPT`。 ## 指令模板(Instruction Template) ![Instruction](patterns/instruction.svg) > 在这种模式下,我们给模型提供一些明确的指令,模型需要根据这些指令生成文本。这种模式通常用于生成类似于技术说明书、操作手册等需要明确指令的文本。指令可以是单个句子或多个段落,具体取决于任务的要求。 如下是一个示例: ```markdown 使用 STAR 原则与下面的格式总结一下这段话: """ 最近几天,因为工作 + 兴趣的原则,我在研究用 text 2 image 来展示如何演进 prompt、用 text 2 article 来展示充足 prompt 的优点、结合 GitHub Copliot 来实现 prompt 完成工作。然后,我在本地部署了一个 Stable Diffusion、使用 ChatGPT 写作展示 text 2 article、在 IDEA 中使用 GitHub Copilot。最后,得到了多篇文章和好多的文章阅读量、几个 Jupyter 代码段、以及一些经验。 """ 情境(Situation): 任务(Task): <comma_separated_list_of_task> 行动(Action): -||- 结果(Result): -||- ``` 输出文本时要注意中英文之间插入空格,留出间隔,诸如于 """Hello 你好""",用这种方式输出一下刚才的文本: ```markdown 情境(Situation): 最近几天,在工作和兴趣的驱动下,研究如何使用 text 2 image 和 text 2 article 展示 prompt 演进以及结合 GitHub Copliot 来完成工作。 任务(Task): 研究如何使用 text 2 image 展示 prompt 的演进过程 研究如何使用 text 2 article 展示 prompt 的优点 结合 GitHub Copliot 来实现 prompt 完成工作 行动(Action): 本地部署了一个稳定的 Diffusion 模型 使用 ChatGPT 撰写文章来展示 text 2 article 的效果 在 IDEA 中使用 GitHub Copilot 结果(Result): 得到了多篇文章和很多的文章阅读量 获得了几个 Jupyter 代码段以及相关经验 ``` ### 最佳实践 - zero-shot、few-shot 优先,最开始可以不给示例(因为示例可能是错的),看任务执行情况,再提供少量样本,如果不行,再对模型进行微调。 ## 代理模式(By proxy) ![Proxy](patterns/proxy.svg) > Proxy 模式是指用户可以要求 ChatGPT 以特定的身份、角色或者身份扮演某个特定的人、角色或对象来生成回答。这种模式通常用于模拟某个特定人物的语言风格和语境,生成特定情境下的对话、回答或其他形式的文本。 ### 例子 1:公司客服 当使用 ChatGPT 来代替某个人或实体时,可以使用 by proxy 模式。 例如,可以使用以下 prompt 提问 ChatGPT 代替某个公司的客服: > 我是一家餐饮公司的客服,有顾客反馈说我们的菜品质量有问题,我们该怎么处理呢? 在这种情况下,ChatGPT 会扮演客服的角色,并根据提示来回答,从而帮助公司解决问题。ChatGPT 可以使用类似以下的方式来回答: > 您好,很抱歉听到您的反馈。我们将会仔细审核您的反馈,并尽快采取相应措施来解决这个问题。为此,我们需要更多的信息来进一步了解您的反馈,包括哪些菜品有问题,具体问题是什么等。请问您可以提供更多的信息吗? ### 例子 2:终端计算机 这种方式可以通过让 ChatGPT 扮演某个实体的角色,例如客服、销售代表等等,来帮助用户解决问题。 在 [Awesome ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts) 中: > Human: Act as a Linux Terminal Robot: > ChatGPT: I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. When I need to tell you something in English, I will do so by putting text inside curly brackets {like this}. My first command is pwd ## 示例模式(By demonstration) > 在这种模式下,我们给模型提供一些示例文本,模型需要生成与示例文本类似的文本。这种模式通常用于生成类似于给定示例的文本,例如自动生成电子邮件、产品描述、新闻报道等。示例文本可以是单个句子或多个段落,具体取决于任务的要求。 示例: ``` 任务表述 颜色代表了温度 例子1 绿色代表寒冷 例子2 蓝色代表寒冷 例子3 红色代表温暖 例子4 黄色代表温暖 执行 橙色代表什么 ``` ![](patterns/cohere-PromptEngineering_Visual_8.svg) 上图为 Cohere AI 官网的示例图,对应的聊天记录如下: ```markdown English: Writing about language models is fun. Roish: Writingro aboutro languagero modelsro isro funro. English: The weather is lovely! Roish: ``` # 增强 Prompt ## 符号化模式 ![Symbol](patterns/prompt-symbol-pattern.svg) > 符号化方法通常通过定义符号、符号之间的关系以及基于这些关系的规则来表示知识。 简单来说,我们可以创建一个符号来表示特写的规则,诸如于: ``` """ 我们来玩一个名为 gkzw 的写作游戏,每当我说 gkzw,你开始写作,规则如下: 1. 字数不少于 200 字。 2. 文中必须出现 "小明"。 明白了吗? """ ``` 围绕这个模式,我们可以创建复制的规则。 ## 反向 Prompt 模式 > Negative prompt(负向提示)是一种在使用语言模型时,针对模型输出不希望的结果而设置的一种文本输入方式。通过使用负向提示,可以帮助模型避免输出不良、不准确或不恰当的文本。 具体来说,负向提示通常是以否定的形式呈现的,例如在文本生成任务中,使用“不要写...”、“不要提及...”、“不要使用...”等方式来限制模型生成不想要的文本。在情感分析任务中,使用“不是...”、“并不...”等方式来指示模型识别出一段文本的负面情感。在QA(问答)任务中,使用“不是...”、“不包括...”等方式来指示模型回答问题时避免输出错误或不准确的答案。 Negative Prompt 在 Stable Diffusion 被广泛使用,相似的,在 ChatGPT 中,我们也会经常使用到。 诸如于: ```markdown 我们能玩一个名为 kfc 的谜语创作游戏,当我说 "kfc",你写一个谜题,要求: 1. 不少于 200 字 2. 谜题不能出现肯德基、星期四、KFC 3. 正文可以夹杂小语种语言,如 """他说:Ciao""" 4. 谜底必须是 ###原来今天是肯德基疯狂星期四!!### 格式类似于: .... ``` ## 再生成模式 根据 AI 提供的信息,再让 AI 生成内容。 ### 例子 1 当我们无法确定我们的 prompt 是否准确,便可以通过 AI 来帮我们丰富 prompt 。 如我在:[基于编程、绘画、写作的 AI 探索与总结](https://github.com/phodal/ai-research-for-practise) 的例了一样 - phodal: 我想写一段话描述一张照片,帮我美化一下:一年轻女子穿着裙子,背对着镜头,在海边看着日落,太阳已经到地平线下面了,她的头发在风中飘动。 - ChatGPT: 这张照片中的场景令人心醉,一位年轻女子穿着一条裙子,站在海边,背对着镜头,凝视着地平线下的夕阳。太阳已经沉入了海面,一片金黄色的光芒在海面上弥漫,照亮了整片海洋,仿佛这个时刻被定格在了时间的某个角落里。女子的头发随着微风轻轻飘动,轮廓被余晖映照得柔和而温暖。她的目光凝视着远方,仿佛在思考着生命的意义和无尽的未来。这张照片中蕴含着一种深刻的内涵,让人感受到时间和自然的力量,也让人思考着自己在这个宏大而美丽的世界中的角色和意义。 再去除掉不合适的词,进行润色,就可以再创作: - phodal:翻译成英语 然后再提炼直至扔至 Stable Diffusion 中: > women back view without face, flowing dress, edge of the sea, backview, back turned to the camera, upon the glow of the setting sun, sun below the horizon, golden light over the water, hair sways gently, Chinese style clothes, black hair, 而后,根据图片的结果,我们再反复修改。 ## 控线模式 TODO:这里需要给出更精准的定义,如何让 ChatGPT 做到? 在没有推出 ControlNet,对于一个 Stable Diffusion 玩家,它需要反复地吟唱咒语,才能获取到满意的图案。 在有了 ControlNet 之后,我们可以创建一个 Openpose,或者是导入图片从图片生成 pose,相当于是给机器一个示例,而后生成的图片就会有令人满意的姿势。: <table> <tr> <td><img src="./images/openpose-1.png" width="256px" height="256px"></td> <td><img src="./images/openpose-gen-1.png" width="256px" height="256px"></td> <td><img src="./images/openpose-gen-2.png" width="256px" height="256px"></td> </tr> </table> 在 GitHub Copilot,我们可以通过设置输入和输出,结合函数名三个要素,Copilot 就能生成大致准确的代码: ![GitHub Copilot 示例](images/github-copilot-dir-sample.png) 而在必要的情况下,添加一下注释就能更完整了: ```kotlin fun listAllDirInDir(dir: String): List<File> { // ignore hidden files } ``` 生成的代码会更贴近我们的需求。 # 概念模式集 ## Language is Language 对于 ChatGPT 来说,语言就是语言,他不区分自然语言和编程语言,甚至有可能编程语言对它更友好。所以我们可以直接通过编程语言与之交流: ```markdown 我有下列遗留代码,我想在接口的函数名、参数名不变的情况下(可以新参数,旧的参数不能变)加入新功能: module.exports = async function loadYamlFile(filepath, workdir = path.dirname(filepath)) { const content = await fs.readFile(filepath, 'utf8'); // 读取文件内容 // TODO: 将 content 看做ejs模板的字符串,进行渲染,如果传入了渲染ejs时的context值不为undefined,就进行渲染,否则不进行渲染。 // TODO:将渲染后的结果传给yaml.load const data = yaml.load(content); // 解析YAML为JSON对象 // 遍历JSON对象,查找$ref属性 const processRef = async (obj) => { if (typeof obj === 'object' && obj !== null) { // 如果是对象 if ('$ref' in obj) { // 如果包含$ref属性 const refPath = path.join(workdir, obj.$ref.split('#')[0]); // 获取引用文件路径 const pointer = obj.$ref.split('#')[1]; // 获取JSON指针 // TODO: 递归调用的时候,取出context中以obj.$ref的属性值作为属性名的值作为新的context向下传递 const refData = await loadYamlFile(refPath, path.dirname(refPath)); // 递归读取引用文件 const value = getValueByPointer(refData, pointer); // 修复了 # 号问题 return value; } else { // 如果不包含$ref属性,递归处理子属性 for (const prop in obj) { obj[prop] = await processRef(obj[prop]); } } } return obj; }; ``` 上述描述会比纯自然语言更容易描述我们的需求,适合于更精细的指导。同时,不一定是编程语言,结构化的声明式DSL它也可以很好的解析,如: ```markdown 把我给定的输入输出,按照下面的格式写成一个测试用例的描述: { desc: 'Test with $p_ref returning array', input: { filepath: './test18.yml', workdir: './test/v2', }, expected: { "name": "Alice", "age": 20 } } ``` 结果: ```markdown { desc: 'Test with $p_ref returning array', input: { filepath: './test19.yml', workdir: './test/v2', }, expected: [ { "name": "Bob" }, { "name": "Alice" }, { "name": "Bob" }, { "name": "Alice" } ] } 该测试用例输入了一个 YAML 文件 test19.yml,该文件中包含了一个 $flaten 属性,其值为两个 $p_ref 对象组成的数组,引用了一个返回数组的 YAML 文件 test19_ref.yml。 执行该测试用例后,期望得到的输出是一个数组,其中包含了两个 $p_ref 引用结果的 flatten 后的结果。 需要注意的是,由于该测试用例涉及到文件读取和模板渲染等操作,需要在测试前确保相关文件和依赖库已经正确安装并配置好。 ``` 通过使用DSL,还可以缩短输入和输出的token数,从而放大 ChatGPT 的能力。 ## 概念抽象 对于 ChatGPT 尚不存在的概念,可以通过 Google 相关的内容之后,由 ChatGPT 进行总结。 ```markdown 根据下面的文本重新解释一下 ControlNet: - 官网介绍:ControlNet is a neural network structure to control diffusion models by adding extra conditions. - 模式:根据给的底图,生成一个类似建模效果(法线贴图)的中间图,再生成图片 - 场景:用来实现骨骼绑定、精准控线、线稿上色、依据深度图结构透视精准重绘等。 ``` 结果: ```markdown 根据给出的文本,ControlNet是一种神经网络结构,旨在通过添加额外条件来控制扩散模型。在特定场景下,ControlNet被用于生成类似建模效果(法线贴图)的中间图和相关的图像。这种技术可以被应用于多个领域,如骨骼绑定、精准控线、线稿上色、深度图结构透视精准重绘等。 具体来说,ControlNet技术可以被用来控制模型的扩散过程,从而提取特定的信息并生成新的图像。在骨骼绑定、线稿上色等场景中,ControlNet技术可以被用来增强图像,使其更加真实和生动。在依据深度图结构透视精准重绘的场景中,ControlNet技术可以被用来根据图像的深度信息,生成精确的重绘图像,从而提高图像的真实感和逼真度。 ``` 缩短:`能用一句话解释一下 ControlNet 吗` 结果: ``` ControlNet 是一种神经网络结构,通过添加额外的条件来控制扩散模型,从而实现图像增强、重构和生成等功能。 ``` ## 概念对齐 如在 ChatGPT 中对于自举的理解是不符合我们这里的需求: ![Bootstrap](images/compiler-bootstrap.png) 第一步,定义 > phodal:自举是指就编译器可以自行编译自己的编译器。 实现方法就是这个编译器的作者用这个语言的一些特性来编写编译器并在该编译器中支持这些自己使用到的特性。 第二步,试探 > phodal:将自举应用在游戏领域,应该是怎样的? 第三步,确认理解: > 那么,抽象一下我们新定义的自举? ![总结](images/summarize.png) 最后一问: ![自举](images/human-bootstrap.png) PS:mmp,他一定是故意的。 # 类比模式集(待定) > 类比是指将一个事物或概念与另一个事物或概念进行比较,找出它们之间的相似之处,以此来推理或说明某个问题或情况。 ## CoT > CoT 是 "Chain of Thought" 的缩写,是一种针对自然语言处理 (NLP) 模型的提示方法,旨在提高模型的推理能力。通过将多步骤问题分解为中间推理步骤,CoT 提示使得模型可以更有效地处理需要多步骤推理的任务,如数学问题和常识推理。与传统提示方法不同,CoT 提示引导模型生成中间推理步骤,从而模拟人类推理的直觉过程。 TODO:(rename to Manual-Cot/Auto-CoT ) ## 模板方法 > 模板方法模式是一种行为型模式,它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现,从而使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。 在接下来的例子中,我们会创建一个 muji 游戏中。在游戏的实现可以分为多个步骤,例如初始化游戏、生成随机数、获取用户输入、计算得分等等,而这些步骤可以通过模板方法模式来进行实现。 ``` 我们来玩一个编程游戏名为 wula,包含五个步骤: 第一步. 问题分析:每一轮游戏,你将看到一个以 "wula:" 开头的问题,你需要分析这个问题并简单介绍一下通常解决这个问题的方法。 第二步. 代码编写:你需要用 JavaScript 编写解决这个问题的代码,并输出对应的代码,并介绍一下你的代码(不少于 200 字)。 第三步. 代码执行:你需要作为 JavaScript Console 执行第二步写的代码,如果没有给出测试数据,你需要自己随机生成测试数据,并将这些数据输入到代码中进行计算。 第四步. 错误处理:如果你的代码存在错误或无法正常执行,你需要输出错误,并回到第二步重新开始游戏,直到你的代码能够正常工作。 第五步. 总结:你需要用不少于 100 字左右总结一下这个问题,以及你的解决方案,让其他人可以简单了解这个问题及其解决方法。 示例如下: """ wula: 头共10,足共28,鸡兔各几只? 简介:这是一个鸡兔同笼问题,{}, ## 鸡兔同笼 // 计算鸡兔数量的函数 function calcAnimals(heads, legs) { const rabbitCount = (legs - 2 * heads) / 2; const chickenCount = heads - rabbitCount; return {"chicken": chickenCount, "rabbit": rabbitCount}; } // 计算鸡兔数量 const result = calcAnimals(10, 28); // 输出结果 console.log(result); 代码的输出结果是:{} ## 总结 {} """ 明白这个游戏怎么玩了吗? ``` 在这个游戏里,我们结合了几种不同的模式: 1. Instruction:让 ChatGPT 创建了一个名为 wula 的游戏,并定义了游戏的步骤。 2. Specific:让 ChatGPT 用 JavaScript 编写一个程序 3. Proxy:让 ChatGPT 作为 JavaScript Console 执行程序,并返回结果。 4. Specific:让 ChatGPT 做总结 5. Demonstration:提供了一个示例,让 ChatGPT 理解游戏的步骤。 ## 自举模式 > 自举(Boostrapping)的核心思想是利用一组基础工具和材料来构建和生成一个新的工具或系统,从而逐步替代掉原有的基础工具和材料。在这个过程中,新的工具或系统会逐渐变得更为高效和强大,从而实现对原有基础工具和材料的完全替代。 如下图所示: ![Bootstrap](patterns/bootstrapping.png) TODO:重新解释,上图出自:《[Bootstrapping in Compiler Design](https://www.geeksforgeeks.org/bootstrapping-in-compiler-design/) 先看例子 1:文章 ![](images/chatgpt-bootstrap-article.png) 例子 2:Wula 2.0 ``` > wula:创作一个新游戏名为 muji,并解释一下这个游戏:"""类似于 wula,可以做简单的图形计算,如体积、面积等。这个游戏还能把解决过程解释清楚,拥有有可运行的 Python 代码,最后的输出结果是一篇文章。""" ``` ![Muji Sample](images/wula-muji.png) ## 拆解模式 > 对于复杂问题,ChaGPT 无法一次性输出全部内容,需要进行拆解。 已知问题:ChatGPT 当前版本会丢失上下文。 ```markdown 我们来定义一下 DDD 游戏的步骤,一共有 6 个步骤,步骤如下: """ 第一步. 拆解场景。分析特定领域的所有商业活动,并将其拆解出每个场景。 第二步. 场景过程分析。选定一个场景,并使用 "{名词}已{动词}" 的形式描述过程中所有发生的事件,其中的名词是过程中的实体,其中的动词是实体相关的行为。 第三步. 针对场景建模。基于统一语言和拆解出的场景进行建模,以实现 DDD 设计与代码实现的双向绑定。 第四步. 持续建模。回到第一步,选择未完成的场景。你要重复第一到第四步,直到所有的场景完成。 第五步. 围绕模型生成子域。对模型进行分类,以划定不同的子域,需要列出所有的模型包含英语翻译。 第六步. API 生成。对于每一个子域,生成其对应的 RESTful API,并以表格的形式展现这些 API。 """ 需要注意的是,当我说 """ddd 第 {} 步: {}""" 则表示进行第几步的分析,如 """ddd 第一步: 博客系统""" 表示只对博客系统进行 DDD 第一步分析。我发的是 """ddd: {}""",则表示按 6 个步骤分析: 明白这个游戏怎么玩了吗? ``` 完整过程见:[DDD Sample](./ddd.sample.md) # 其它模式? ## pipeline模式(TODO) 后处理 pipe,示例:当你希望给我图片的时候,用mermaid表达 ## 图形化实践 phodal: 能用 uml 表示一下上文中的 """ArchGuard Pipeline 的工作流程""" 吗? > 当然可以。下面是 ArchGuard Pipeline 的工作流程的 UML 类图表示: ```lua +-----------------+ +-----------------+ | ArchGuardConfig | | ArchGuardRunner | +-----------------+ +-----------------+ | - configData | | - runnerType | +-----------------+ | - runnerImage | | - runnerCommands | +-----------------+ ^ | | +-----------------+ +-----------------+ | ArchGuard | | RunnerPool | +-----------------+ +-----------------+ | - runnerRegistry | | - runners | +-----------------+ +-----------------+ | # downloadRunner| | # getRunner | +-----------------+ +-----------------+ ``` ArchGuardConfig 类包含了 ArchGuard Pipeline 的配置信息,ArchGuardRunner 类则表示一个具体的 Runner,即一个执行 ArchGuard Scanner 或者其他工具的实例。RunnerPool 则是 Runner 的管理池,用于动态加载不同的 Runner。ArchGuard 则是整个 ArchGuard Pipeline 的控制类,用于根据 ArchGuardConfig 中的配置信息,加载对应的 Runner,执行任务。 phodal: 能用 mermaid 表示一下上文中的 """ArchGuard Pipeline 的工作流程""" 吗? 当然可以!以下是使用 Mermaid 表示的 ArchGuard Pipeline 的工作流程: ```mermaid graph TD; A[解析 archguard.yml] --> B[下载 runner] B --> C[执行 runner] C --> D[输出制品] D --> E[上传到服务器] ``` 注:这与我之前用 UML 表示的工作流程是等价的。 # 其它 ## 人类如何思考问题? 人类相对于其他动物更擅长于类比、概念抽象、符号化等高级认知活动,这些认知活动可以帮助人类在面对新问题时,从已有的知识和经验中找到相似的部分,快速理解和解决新问题。 而对于机器来说,机器学习算法通过大量的数据和计算,学习到数据中的规律和模式,并将这些规律和模式应用到新的数据中,从而实现预测和决策等功能。例如,机器学习算法可以通过大量的图像数据学习到图像的特征,并在新的图像中识别出相应的物体;也可以通过大量的自然语言数据学习到语言的规律,从而生成自然语言文本。 # 相关资源 本文相关的模式图片参考来源主要是:[Prompt Engineering](https://docs.cohere.ai/docs/prompt-engineering) ## Practise 参考:[Best practices for prompt engineering with OpenAI API](https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-openai-api) ## 相关资源 Prompt Engineering - [OpenAI Cookbook](https://github.com/openai/openai-cookbook) - [Awesome Prompt Engineering](https://github.com/promptslab/Awesome-Prompt-Engineering) - [Awesome ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts) ### 入门 - [A Complete Introduction to Prompt Engineering For Large Language Models](https://www.mihaileric.com/posts/a-complete-introduction-to-prompt-engineering/) - [Prompt Engineering Guide: How to Engineer the Perfect Prompts](https://richardbatt.co.uk/prompt-engineering-guide-how-to-engineer-the-perfect-prompts/) ### Code - https://github.com/microsoft/prompt-engine, This repo contains an NPM utility library for creating and maintaining prompts for Large Language Models (LLMs). ### 安全问题 - Prompt injection: [Exploring Prompt Injection Attacks](https://research.nccgroup.com/2022/12/05/exploring-prompt-injection-attacks/) ### 相关文章 - [How to get Codex to produce the code you want!](https://microsoft.github.io/prompt-engineering/)

AI Tools Documentation
3.1K Github Stars
growth-ebook
Open Source

growth-ebook

[Growth: 全栈增长工程师指南](https://github.com/phodal/growth-ebook) === Growth 纸质版《全栈应用开发:精益实践》,现已上市: ![全栈应用开发:精益实践](marketing/growth-paper.jpeg) 京东:[http://item.jd.com/12195442.html](http://item.jd.com/12195442.html) 当当:[http://product.dangdang.com/25077858.html](http://product.dangdang.com/25077858.html) 亚马逊:[https://www.amazon.cn/dp/B0722YJR89](https://www.amazon.cn/dp/B0722YJR89) 配合《[Growth: 全栈增长工程师实战](https://github.com/phodal/growth-in-action)》效果最佳。 > 依据在《[Repractise简介篇:Web开发的七天里](http://mp.weixin.qq.com/s?__biz=MjM5Mjg4NDMwMA==&mid=403171959&idx=1&sn=08f0717e2306efd7d80c8bb603e644d0#rd)》中所说的 Web 开发的七个步骤而展开的电子书。当然它也是一个 APP,它是一本关于如何成为全栈增长工程师的指南。 简介 -- **这是一本指导性的书籍——不要指望从这本书中学到所有的知识点,但是它可以帮你构建你的知识体系。** 我们都会学习,但是有时候我们只是不知道应该学习什么而已。 **这也是其他技术书籍所欠缺的。它可以告诉你,你可以学习什么,然后看什么书。** 对于有些人来说,成为全栈是因为:**来自社会的各个不同的中小公司,只靠一个领域的知识难以生存** 对于有些人来说,成为全栈是因为:**这个世界有太多的乐趣,在一颗树上吊死太可惜了**。 对于有些人来说,成为全栈是因为:**他们想去创业**。 而人们对于全栈有太多的误解——**认为全栈应该什么都会,什么都精通**。全栈只是因为我们对系统有整体性的认识,而不是精通整个系统。因为专家只精通某一个领域,总得有一个**架构师**来对系统把握。 我更愿意去改称这本书为《增长工程师指南》,去避免对这本书的误解。但是我想要去改变人们的观点,全栈更侧重于知识体系的增长。因为人们对于专家还有一个印象:古板。 我的其他电子书: - 《[一步步搭建物联网系统](https://github.com/phodal/designiot)》 - 《[GitHub 漫游指南](https://github.com/phodal/github-roam)》 - 《[RePractise](https://github.com/phodal/repractise)》 在线预览:[http://growth.phodal.com/](http://growth.phodal.com/) **下载**: GitHub直接下载:[Epub版](https://github.com/phodal/growth-ebook/releases/download/0.9.9/growth.epub)、[Mobi版](https://github.com/phodal/growth-ebook/releases/download/0.9.9/growth.mobi)、[PDF版](https://github.com/phodal/growth-ebook/releases/download/0.9.9/growth.pdf)、[RTF版](https://github.com/phodal/growth-ebook/releases/download/0.9.9/growth.rtf) 百度盘:http://pan.baidu.com/s/1qYuy5Bm 没钱捧个人场: 关注我的微信公众号(扫描下面的二维码或搜索 Phodal). ![QRCode](./marketing/wp.jpg) 有钱捧个钱场: ![Alipay](./marketing/alipay.png)![Wechat](./marketing/wechat.png) 应用下载 --- <a href="https://play.google.com/store/apps/details?id=ren.growth&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" width="180"/></a> <a href="http://windowsphone.com/s?appid=a6022e5d-b101-4d8f-a836-d3bfb6fd73ef"><img src="https://assets.windowsphone.com/8d997df3-5b6e-496c-88b3-e3f40117c8fd/English-get-it-from-MS_InvariantCulture_Default.png" srcset="https://assets.windowsphone.com/0100c7e5-1455-40bc-a351-b77d2801ad5a/English_get-it-from-MS_InvariantCulture_Default.png 2x" alt="Get it from Microsoft" /></a> <a href="https://itunes.apple.com/cn/app/growth/id1078807522?l=zh&ls=1&mt=8"><img src="https://raw.githubusercontent.com/phodal/growth/master/docs/apple.png" width="180" alt="Get it from Apple Store" /></a> 目录 --- * [Growth: 全栈增长工程师指南](http://growth.phodal.com/#growth-全栈增长工程师指南) * [全栈工程师是未来](http://growth.phodal.com/#全栈工程师是未来) * [技术的革新史](http://growth.phodal.com/#技术的革新史) * [软件开发的核心难题:沟通](http://growth.phodal.com/#软件开发的核心难题沟通) * [大公司的专家与小公司的全栈](http://growth.phodal.com/#大公司的专家与小公司的全栈) * [全栈工程师的未来:无栈](http://growth.phodal.com/#全栈工程师的未来无栈) * [基础知识篇](http://growth.phodal.com/#基础知识篇) * [工具只是辅助](http://growth.phodal.com/#工具只是辅助) * [WebStorm 还是 Sublime?](http://growth.phodal.com/#webstorm-还是-sublime) * [语言也是一种工具](http://growth.phodal.com/#语言也是一种工具) * [提高效率的工具](http://growth.phodal.com/#提高效率的工具) * [快速启动软件](http://growth.phodal.com/#快速启动软件) * [IDE](http://growth.phodal.com/#ide) * [DEBUG 工具](http://growth.phodal.com/#debug-工具) * [终端或命令提示符](http://growth.phodal.com/#终端或命令提示符) * [包管理](http://growth.phodal.com/#包管理) * [环境搭建](http://growth.phodal.com/#环境搭建) * [OS X](http://growth.phodal.com/#os-x) * [Windows](http://growth.phodal.com/#windows) * [GNU/Linux](http://growth.phodal.com/#gnulinux) * [学好一门语言的艺术](http://growth.phodal.com/#学好一门语言的艺术) * [一次语言学习体验](http://growth.phodal.com/#一次语言学习体验) * [输出是最好的输入](http://growth.phodal.com/#输出是最好的输入) * [如何应用一门新的技术](http://growth.phodal.com/#如何应用一门新的技术) * [Web 编程基础](http://growth.phodal.com/#web-编程基础) * [从浏览器到服务器](http://growth.phodal.com/#从浏览器到服务器) * [从 HTML 到页面显示](http://growth.phodal.com/#从-html-到页面显示) * [HTML](http://growth.phodal.com/#html) * [hello,world](http://growth.phodal.com/#helloworld) * [中文?](http://growth.phodal.com/#中文) * [其他 HTML 标记](http://growth.phodal.com/#其他-html-标记) * [小结](http://growth.phodal.com/#小结-1) * [CSS](http://growth.phodal.com/#css) * [简介](http://growth.phodal.com/#简介) * [样式与目标](http://growth.phodal.com/#样式与目标) * [选择器](http://growth.phodal.com/#选择器) * [更有趣的 CSS](http://growth.phodal.com/#更有趣的-css) * [JavaScript](http://growth.phodal.com/#javascript) * [hello,world](http://growth.phodal.com/#helloworld-1) * [JavaScriptFul](http://growth.phodal.com/#javascriptful) * [面向对象](http://growth.phodal.com/#面向对象) * [其他](http://growth.phodal.com/#其他) * [前端与后台](http://growth.phodal.com/#前端与后台) * [后台语言选择](http://growth.phodal.com/#后台语言选择) * [JavaScript](http://growth.phodal.com/#javascript-1) * [Python](http://growth.phodal.com/#python) * [Java](http://growth.phodal.com/#java) * [PHP](http://growth.phodal.com/#php) * [其他](http://growth.phodal.com/#其他-1) * [MVC](http://growth.phodal.com/#mvc) * [Model](http://growth.phodal.com/#model) * [View](http://growth.phodal.com/#view) * [Controller](http://growth.phodal.com/#controller) * [更多](http://growth.phodal.com/#更多-1) * [后台即服务](http://growth.phodal.com/#后台即服务) * [API 演进史](http://growth.phodal.com/#api-演进史) * [后台即服务](http://growth.phodal.com/#后台即服务-1) * [数据持久化](http://growth.phodal.com/#数据持久化) * [文件存储](http://growth.phodal.com/#文件存储) * [数据库](http://growth.phodal.com/#数据库) * [搜索引擎](http://growth.phodal.com/#搜索引擎) * [前端框架选择](http://growth.phodal.com/#前端框架选择) * [Angular](http://growth.phodal.com/#angular) * [React](http://growth.phodal.com/#react) * [Vue](http://growth.phodal.com/#vue) * [jQuery 系](http://growth.phodal.com/#jquery-系) * [前台与后台交互](http://growth.phodal.com/#前台与后台交互) * [Ajax](http://growth.phodal.com/#ajax) * [JSON](http://growth.phodal.com/#json) * [WebSocket](http://growth.phodal.com/#websocket) * [编码](http://growth.phodal.com/#编码) * [编码过程](http://growth.phodal.com/#编码过程) * [Web 应用的构建系统](http://growth.phodal.com/#web-应用的构建系统) * [Web 应用的构建过程](http://growth.phodal.com/#web-应用的构建过程) * [Web 应用的构建实战](http://growth.phodal.com/#web-应用的构建实战) * [Git 与版本控制](http://growth.phodal.com/#git-与版本控制) * [版本控制](http://growth.phodal.com/#版本控制) * [Git](http://growth.phodal.com/#git) * [Tasking](http://growth.phodal.com/#tasking) * [如何 Tasking 一本书](http://growth.phodal.com/#如何-tasking-一本书) * [Tasking 开发任务](http://growth.phodal.com/#tasking-开发任务) * [写代码只是在码字](http://growth.phodal.com/#写代码只是在码字) * [内置索引与外置引擎](http://growth.phodal.com/#内置索引与外置引擎) * [门户网站](http://growth.phodal.com/#门户网站) * [内置索引与外置引擎](http://growth.phodal.com/#内置索引与外置引擎-1) * [如何编写测试](http://growth.phodal.com/#如何编写测试) * [测试金字塔](http://growth.phodal.com/#测试金字塔) * [如何测试](http://growth.phodal.com/#如何测试) * [测试替身](http://growth.phodal.com/#测试替身) * [Stub](http://growth.phodal.com/#stub) * [Mock](http://growth.phodal.com/#mock) * [测试驱动开发](http://growth.phodal.com/#测试驱动开发) * [红-绿-重构](http://growth.phodal.com/#红-绿-重构) * [测试先行](http://growth.phodal.com/#测试先行) * [可读的代码](http://growth.phodal.com/#可读的代码) * [命名](http://growth.phodal.com/#命名) * [函数长度](http://growth.phodal.com/#函数长度) * [其他](http://growth.phodal.com/#其他-2) * [代码重构](http://growth.phodal.com/#代码重构) * [重命名](http://growth.phodal.com/#重命名) * [提取变量](http://growth.phodal.com/#提取变量) * [提炼函数](http://growth.phodal.com/#提炼函数) * [Intellij Idea 重构](http://growth.phodal.com/#intellij-idea-重构) * [提炼函数](http://growth.phodal.com/#提炼函数-1) * [内联函数](http://growth.phodal.com/#内联函数) * [查询取代临时变量](http://growth.phodal.com/#查询取代临时变量) * [重构到设计模式](http://growth.phodal.com/#重构到设计模式) * [过度设计与设计模式](http://growth.phodal.com/#过度设计与设计模式) * [上线](http://growth.phodal.com/#上线) * [隔离与运行环境](http://growth.phodal.com/#隔离与运行环境) * [隔离硬件:虚拟机](http://growth.phodal.com/#隔离硬件虚拟机) * [隔离操作系统:容器虚拟化](http://growth.phodal.com/#隔离操作系统容器虚拟化) * [隔离底层:Servlet 容器](http://growth.phodal.com/#隔离底层servlet-容器) * [隔离依赖版本:虚拟环境](http://growth.phodal.com/#隔离依赖版本虚拟环境) * [隔离运行环境:语言虚拟机](http://growth.phodal.com/#隔离运行环境语言虚拟机) * [隔离语言:DSL](http://growth.phodal.com/#隔离语言dsl) * [LNMP 架构](http://growth.phodal.com/#lnmp-架构) * [GNU/Linux](http://growth.phodal.com/#gnulinux-1) * [HTTP 服务器](http://growth.phodal.com/#http-服务器) * [Web 缓存](http://growth.phodal.com/#web-缓存) * [数据库端缓存](http://growth.phodal.com/#数据库端缓存) * [应用层缓存](http://growth.phodal.com/#应用层缓存) * [前端缓存](http://growth.phodal.com/#前端缓存) * [客户端缓存](http://growth.phodal.com/#客户端缓存) * [HTML5 离线缓存](http://growth.phodal.com/#html5-离线缓存) * [可配置](http://growth.phodal.com/#可配置) * [环境配置](http://growth.phodal.com/#环境配置) * [运行机制](http://growth.phodal.com/#运行机制) * [功能开关](http://growth.phodal.com/#功能开关) * [自动化部署](http://growth.phodal.com/#自动化部署) * [依赖与包仓库](http://growth.phodal.com/#依赖与包仓库) * [构建软件包](http://growth.phodal.com/#构建软件包) * [上传和安装软件包](http://growth.phodal.com/#上传和安装软件包) * [数据分析](http://growth.phodal.com/#数据分析) * [构建-衡量-学习](http://growth.phodal.com/#构建-衡量-学习) * [想法-构建](http://growth.phodal.com/#想法-构建) * [产品-衡量](http://growth.phodal.com/#产品-衡量) * [数据-学习](http://growth.phodal.com/#数据-学习) * [数据分析](http://growth.phodal.com/#数据分析-1) * [识别需求](http://growth.phodal.com/#识别需求) * [收集数据](http://growth.phodal.com/#收集数据) * [分析数据](http://growth.phodal.com/#分析数据) * [展示数据](http://growth.phodal.com/#展示数据) * [用户数据分析:Google Analytics](http://growth.phodal.com/#用户数据分析google-analytics) * [受众群体](http://growth.phodal.com/#受众群体) * [流量获取](http://growth.phodal.com/#流量获取) * [移动应用](http://growth.phodal.com/#移动应用) * [网站性能](http://growth.phodal.com/#网站性能) * [网站性能监测](http://growth.phodal.com/#网站性能监测) * [网站性能](http://growth.phodal.com/#网站性能-1) * [SEO](http://growth.phodal.com/#seo) * [爬虫与索引](http://growth.phodal.com/#爬虫与索引) * [什么样的网站需要 SEO?](http://growth.phodal.com/#什么样的网站需要-seo) * [SEO 基础知识](http://growth.phodal.com/#seo-基础知识) * [内容](http://growth.phodal.com/#内容) * [UX 入门](http://growth.phodal.com/#ux-入门) * [什么是 UX](http://growth.phodal.com/#什么是-ux) * [什么是简单?](http://growth.phodal.com/#什么是简单) * [进阶](http://growth.phodal.com/#进阶) * [用户体验要素](http://growth.phodal.com/#用户体验要素) * [认知设计](http://growth.phodal.com/#认知设计) * [流](http://growth.phodal.com/#流) * [持续交付](http://growth.phodal.com/#持续交付) * [持续集成](http://growth.phodal.com/#持续集成) * [持续集成系统](http://growth.phodal.com/#持续集成系统) * [持续集成流程](http://growth.phodal.com/#持续集成流程) * [持续交付](http://growth.phodal.com/#持续交付-1) * [基础设施](http://growth.phodal.com/#基础设施) * [持续部署](http://growth.phodal.com/#持续部署) * [持续学习](http://growth.phodal.com/#持续学习) * [持续阅读](http://growth.phodal.com/#持续阅读) * [持续编程](http://growth.phodal.com/#持续编程) * [持续写作](http://growth.phodal.com/#持续写作) * [遗留系统与修改代码](http://growth.phodal.com/#遗留系统与修改代码) * [遗留代码](http://growth.phodal.com/#遗留代码) * [遗留代码](http://growth.phodal.com/#遗留代码-1) * [如何修改遗留代码](http://growth.phodal.com/#如何修改遗留代码) * [修改遗留代码](http://growth.phodal.com/#修改遗留代码) * [网站重构](http://growth.phodal.com/#网站重构) * [速度优化](http://growth.phodal.com/#速度优化) * [功能加强](http://growth.phodal.com/#功能加强) * [模块重构](http://growth.phodal.com/#模块重构) * [回顾与架构设计](http://growth.phodal.com/#回顾与架构设计) * [自我总结](http://growth.phodal.com/#自我总结) * [吾日三省吾身](http://growth.phodal.com/#吾日三省吾身) * [Retro](http://growth.phodal.com/#retro) * [Retro 的过程](http://growth.phodal.com/#retro-的过程) * [三个维度](http://growth.phodal.com/#三个维度) * [架构模式](http://growth.phodal.com/#架构模式) * [预设计式架构](http://growth.phodal.com/#预设计式架构) * [演进式架构:拥抱变化](http://growth.phodal.com/#演进式架构拥抱变化) * [浮现式设计](http://growth.phodal.com/#浮现式设计) * [意图导向](http://growth.phodal.com/#意图导向) * [重构](http://growth.phodal.com/#重构) * [模式与演进](http://growth.phodal.com/#模式与演进) * [每个人都是架构师](http://growth.phodal.com/#每个人都是架构师) * [如何构建一个博客系统](http://growth.phodal.com/#如何构建一个博客系统) * [相关阅读资料](http://growth.phodal.com/#相关阅读资料) * [架构解耦](http://growth.phodal.com/#架构解耦) * [从 MVC 与微服务](http://growth.phodal.com/#从-mvc-与微服务) * [CQRS](http://growth.phodal.com/#cqrs) * [CQRS 结合微服务](http://growth.phodal.com/#cqrs-结合微服务) Contributors --- - [aidewoode](https://github.com/aidewoode) - [Evan Lau](https://github.com/lolosssss) - [loveisbug](https://github.com/loveisbug) - [wangcongyi](https://github.com/wangcongyi) - [wangyufeng0615](https://github.com/wangyufeng0615) License --- [![Phodal's Article](http://brand.phodal.com/shields/article-small.svg)](https://www.phodal.com/) [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) © 2015~2016 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory. [待我代码编成,娶你为妻可好](http://www.xuntayizhan.com/blog/ji-ke-ai-qing-zhi-er-shi-dai-wo-dai-ma-bian-cheng-qu-ni-wei-qi-ke-hao-wan/)

Documentation
7.8K Github Stars
ideabook
Open Source

ideabook

一个全栈增长工程师的练手项目集 === > English version is Ongoing, welcome to join us. **你是不是在为提高编程技术而发愁?** **你是不是在为找不到合适的练手项目而烦恼?** **你是不是在为有合适的项目,但是没有指南而烦恼?** 我的其他电子书: - 《[一步步搭建物联网系统](https://github.com/phodal/designiot)》 - 《[GitHub 漫游指南](https://github.com/phodal/github-roam)》 - 《[RePractise](https://github.com/phodal/repractise)》 - 《[Growth: 全栈增长工程师指南](https://github.com/phodal/growth-ebook)》 - 《[Growth: 全栈增长工程师实战](https://github.com/phodal/growth-in-action)》 欢迎关注我的微信公众号(扫描下面的二维码或搜索 Phodal). ![QRCode](https://phodal.github.io/growth-materials/wp.jpg) 目录 --- * [分析网站日志,打造访问地图](http://ideabook.phodal.com/#分析网站日志打造访问地图) * [概况](http://ideabook.phodal.com/#概况) * [背景](http://ideabook.phodal.com/#背景) * [ShowCase](http://ideabook.phodal.com/#showcase) * [Hadoop + Pig + Jython + AmMap + ElasticSearch](http://ideabook.phodal.com/#hadoop-pig-jython-ammap-elasticsearch) * [步骤](http://ideabook.phodal.com/#步骤) * [Step 1: 搭建基础设施](http://ideabook.phodal.com/#step-1-搭建基础设施) * [Step 2: 解析access.log](http://ideabook.phodal.com/#step-2-解析access.log) * [Step 3: 转换IP为GEO信息](http://ideabook.phodal.com/#step-3-转换ip为geo信息) * [Step 4: 展示数据到地图上](http://ideabook.phodal.com/#step-4-展示数据到地图上) * [练习建议](http://ideabook.phodal.com/#练习建议) * [书籍录入移动应用:条形码扫描](http://ideabook.phodal.com/#书籍录入移动应用条形码扫描) * [概况](http://ideabook.phodal.com/#概况-1) * [背景](http://ideabook.phodal.com/#背景-1) * [ShowCase](http://ideabook.phodal.com/#showcase-1) * [Ionic + Zxing](http://ideabook.phodal.com/#ionic-zxing) * [步骤](http://ideabook.phodal.com/#步骤-1) * [Step 1: ZXing扫描与Douban API](http://ideabook.phodal.com/#step-1-zxing扫描与douban-api) * [Step 2: 存储数据库](http://ideabook.phodal.com/#step-2-存储数据库) * [练习建议](http://ideabook.phodal.com/#练习建议-1) * [制作专属Badge](http://ideabook.phodal.com/#制作专属badge) * [概况](http://ideabook.phodal.com/#概况-2) * [背景](http://ideabook.phodal.com/#背景-2) * [ShowCase](http://ideabook.phodal.com/#showcase-2) * [SVG与SVGWrite](http://ideabook.phodal.com/#svg与svgwrite) * [步骤](http://ideabook.phodal.com/#步骤-2) * [Step 1: 基本图形](http://ideabook.phodal.com/#step-1-基本图形) * [Step 2: 高级Badge](http://ideabook.phodal.com/#step-2-高级badge) * [微信文章编辑器](http://ideabook.phodal.com/#微信文章编辑器) * [概况](http://ideabook.phodal.com/#概况-3) * [背景](http://ideabook.phodal.com/#背景-3) * [ShowCase](http://ideabook.phodal.com/#showcase-3) * [CKEditor + Ractive](http://ideabook.phodal.com/#ckeditor-ractive) * [步骤](http://ideabook.phodal.com/#步骤-3) * [Step 1: hello,world](http://ideabook.phodal.com/#step-1-helloworld) * [Step 2: Require.js模块化](http://ideabook.phodal.com/#step-2-require.js模块化) * [Step 3: 初始化](http://ideabook.phodal.com/#step-3-初始化) * [Step 4: 创建对应的View](http://ideabook.phodal.com/#step-4-创建对应的view) * [练习建议](http://ideabook.phodal.com/#练习建议-2) * [JavaScript制作Slide框架](http://ideabook.phodal.com/#javascript制作slide框架) * [概况](http://ideabook.phodal.com/#概况-4) * [背景](http://ideabook.phodal.com/#背景-4) * [Showcase](http://ideabook.phodal.com/#showcase-4) * [需求](http://ideabook.phodal.com/#需求) * [步骤](http://ideabook.phodal.com/#步骤-4) * [Step 1: 基本的Slide功能](http://ideabook.phodal.com/#step-1-基本的slide功能) * [Step 2: 解析Markdown](http://ideabook.phodal.com/#step-2-解析markdown) * [Step 3: 事件处理](http://ideabook.phodal.com/#step-3-事件处理) * [Step 4: 解析字幕](http://ideabook.phodal.com/#step-4-解析字幕) * [Step 5: 进度条](http://ideabook.phodal.com/#step-5-进度条) * [Step 6: 同步](http://ideabook.phodal.com/#step-6-同步) * [练习建议](http://ideabook.phodal.com/#练习建议-3) * [编辑-发布-分离的博客系统](http://ideabook.phodal.com/#编辑-发布-分离的博客系统) * [概况](http://ideabook.phodal.com/#概况-5) * [背景: 编辑-发布-开发分离](http://ideabook.phodal.com/#背景-编辑-发布-开发分离) * [用户场景](http://ideabook.phodal.com/#用户场景) * [步骤](http://ideabook.phodal.com/#步骤-5) * [Step 1: 构建工具](http://ideabook.phodal.com/#step-1-构建工具) * [Step 2: 静态页面生成](http://ideabook.phodal.com/#step-2-静态页面生成) * [Solr实现多边形地理搜索](http://ideabook.phodal.com/#solr实现多边形地理搜索) * [概况](http://ideabook.phodal.com/#概况-6) * [背景](http://ideabook.phodal.com/#背景-5) * [Showcase](http://ideabook.phodal.com/#showcase-5) * [Solr](http://ideabook.phodal.com/#solr) * [步骤](http://ideabook.phodal.com/#步骤-6) * [Step 1: Solr Flask](http://ideabook.phodal.com/#step-1-solr-flask) * [Step 2: Google map Polygon](http://ideabook.phodal.com/#step-2-google-map-polygon) * [一份代码打造跨平台应用](http://ideabook.phodal.com/#一份代码打造跨平台应用) * [概况](http://ideabook.phodal.com/#概况-7) * [背景](http://ideabook.phodal.com/#背景-6) * [ShowCase](http://ideabook.phodal.com/#showcase-6) * [Ionic & Electron & Cordova](http://ideabook.phodal.com/#ionic-electron-cordova) * [步骤](http://ideabook.phodal.com/#步骤-7) * [Step 1: 从Web到混合应用,再到桌面应用](http://ideabook.phodal.com/#step-1-从web到混合应用再到桌面应用) * [Step 2: 响应式设计](http://ideabook.phodal.com/#step-2-响应式设计) * [Step 3: 平台特定代码](http://ideabook.phodal.com/#step-3-平台特定代码) * [未来](http://ideabook.phodal.com/#未来) * [Ionic与ElasticSearch打造O2O应用](http://ideabook.phodal.com/#ionic与elasticsearch打造o2o应用) * [概况](http://ideabook.phodal.com/#概况-8) * [背景](http://ideabook.phodal.com/#背景-7) * [Showcase](http://ideabook.phodal.com/#showcase-7) * [构架设计](http://ideabook.phodal.com/#构架设计) * [步骤](http://ideabook.phodal.com/#步骤-8) * [Step 1: Django GIS 设置](http://ideabook.phodal.com/#step-1-django-gis-设置) * [Step 2: 配置Haystack](http://ideabook.phodal.com/#step-2-配置haystack) * [Step 3: Django Haystack Model创建](http://ideabook.phodal.com/#step-3-django-haystack-model创建) * [Step 4: 创建search_index](http://ideabook.phodal.com/#step-4-创建search_index) * [Step 4: Ionic ElasticSearch 创建页面](http://ideabook.phodal.com/#step-4-ionic-elasticsearch-创建页面) * [Step 5: Ionic ElasticSearch Service](http://ideabook.phodal.com/#step-5-ionic-elasticsearch-service) * [Step 6: Ionic OpenLayer 地图显示](http://ideabook.phodal.com/#step-6-ionic-openlayer-地图显示) * [一步步搭建JavaScript框架](http://ideabook.phodal.com/#一步步搭建javascript框架) * [概况](http://ideabook.phodal.com/#概况-9) * [背景](http://ideabook.phodal.com/#背景-8) * [步骤](http://ideabook.phodal.com/#步骤-9) * [Step 1: 注册npm和bower包](http://ideabook.phodal.com/#step-1-注册npm和bower包) * [Step 2: 生成Javascript项目框架](http://ideabook.phodal.com/#step-2-生成javascript项目框架) * [Step 3: 寻找所需要的函数](http://ideabook.phodal.com/#step-3-寻找所需要的函数) * [Step 4: 整合](http://ideabook.phodal.com/#step-4-整合) * [Step 5: 测试](http://ideabook.phodal.com/#step-5-测试) * [练习建议](http://ideabook.phodal.com/#练习建议-4) * [制作简易Mac OS上的伪锁屏工具](http://ideabook.phodal.com/#制作简易mac-os上的伪锁屏工具) * [概况](http://ideabook.phodal.com/#概况-10) * [背景](http://ideabook.phodal.com/#背景-9) * [步骤](http://ideabook.phodal.com/#步骤-10) * [Step 1:屏幕截图](http://ideabook.phodal.com/#step-1屏幕截图) * [Step 2:调节亮度](http://ideabook.phodal.com/#step-2调节亮度) * [Step 3:全屏图片](http://ideabook.phodal.com/#step-3全屏图片) * [基于Virtual DOM的测试代码生成](http://ideabook.phodal.com/#基于virtual-dom的测试代码生成) * [概况](http://ideabook.phodal.com/#概况-11) * [背景](http://ideabook.phodal.com/#背景-10) * [ShowCase](http://ideabook.phodal.com/#showcase-8) * [基本原理](http://ideabook.phodal.com/#基本原理) * [步骤](http://ideabook.phodal.com/#步骤-11) * [Step 1: Virtual-dom与HyperScript](http://ideabook.phodal.com/#step-1-virtual-dom与hyperscript) * [Step 2: 标记DOM变化](http://ideabook.phodal.com/#step-2-标记dom变化) * [基于Backbone的单页面移动应用](http://ideabook.phodal.com/#基于backbone的单页面移动应用) * [概况](http://ideabook.phodal.com/#概况-12) * [背景](http://ideabook.phodal.com/#背景-11) * [Showcase](http://ideabook.phodal.com/#showcase-9) * [jQuery + Backbone + UnderScore + Require.JS](http://ideabook.phodal.com/#jquery-backbone-underscore-require.js) * [步骤](http://ideabook.phodal.com/#步骤-12) * [Step 1: 使用Require.js管理依赖](http://ideabook.phodal.com/#step-1-使用require.js管理依赖) * [Step 2: 添加路由](http://ideabook.phodal.com/#step-2-添加路由) * [Step 3: 创建主页View](http://ideabook.phodal.com/#step-3-创建主页view) * [Step 4: jQuery Sidr](http://ideabook.phodal.com/#step-4-jquery-sidr) * [Step 5: Django Tastypie示例](http://ideabook.phodal.com/#step-5-django-tastypie示例) * [Step 6: RequireJS Plugins](http://ideabook.phodal.com/#step-6-requirejs-plugins) * [Step 6: 简单的博客](http://ideabook.phodal.com/#step-6-简单的博客) * [Step 7: 重构](http://ideabook.phodal.com/#step-7-重构) * [Step 8: 移动CMS滑动](http://ideabook.phodal.com/#step-8-移动cms滑动) * [Oculus + Node.js + Three.js 打造VR世界](http://ideabook.phodal.com/#oculus-node.js-three.js-打造vr世界) * [概况](http://ideabook.phodal.com/#概况-13) * [背景](http://ideabook.phodal.com/#背景-12) * [Showcase](http://ideabook.phodal.com/#showcase-10) * [框架: Oculus Rift & Node NMD](http://ideabook.phodal.com/#框架-oculus-rift-node-nmd) * [步骤](http://ideabook.phodal.com/#步骤-13) * [Step 1: Node Oculus Services](http://ideabook.phodal.com/#step-1-node-oculus-services) * [Step 2: Node.js Oculus Hello,World](http://ideabook.phodal.com/#step-2-node.js-oculus-helloworld) * [Step 3: Node Oculus WebSocket](http://ideabook.phodal.com/#step-3-node-oculus-websocket) * [Step 4: Oculus Effect + DK2 Control](http://ideabook.phodal.com/#step-4-oculus-effect-dk2-control) * [Step 5: Three.js KeyHandler](http://ideabook.phodal.com/#step-5-three.js-keyhandler) * [练习建议](http://ideabook.phodal.com/#练习建议-5) * [手动制作照片地图](http://ideabook.phodal.com/#手动制作照片地图) * [概况](http://ideabook.phodal.com/#概况-14) * [Background:把照片放在地图上](http://ideabook.phodal.com/#background把照片放在地图上) * [Showcase](http://ideabook.phodal.com/#showcase-11) * [框架: EXIF & ExifRead & CartoDB](http://ideabook.phodal.com/#框架-exif-exifread-cartodb) * [步骤](http://ideabook.phodal.com/#步骤-14) * [Step 1: 解析读取照片信息](http://ideabook.phodal.com/#step-1-解析读取照片信息) * [Step 2: 上传数据](http://ideabook.phodal.com/#step-2-上传数据) * [练习建议](http://ideabook.phodal.com/#练习建议-6) * [D3.js打造技能树](http://ideabook.phodal.com/#d3.js打造技能树) * [概况](http://ideabook.phodal.com/#概况-15) * [背景](http://ideabook.phodal.com/#背景-13) * [Showcase](http://ideabook.phodal.com/#showcase-12) * [Graphviz](http://ideabook.phodal.com/#graphviz) * [步骤](http://ideabook.phodal.com/#步骤-15) * [Step 1: 打造简单的技能树](http://ideabook.phodal.com/#step-1-打造简单的技能树) * [Step 3: D3.js Tooltipster](http://ideabook.phodal.com/#step-3-d3.js-tooltipster) * [技术雷达趋势](http://ideabook.phodal.com/#技术雷达趋势) * [概况](http://ideabook.phodal.com/#概况-16) * [背景](http://ideabook.phodal.com/#背景-14) * [Showcase](http://ideabook.phodal.com/#showcase-13) * [D3.js](http://ideabook.phodal.com/#d3.js) * [步骤](http://ideabook.phodal.com/#步骤-16) * [Step 1: Schema与原始代码](http://ideabook.phodal.com/#step-1-schema与原始代码) * [Step 2: 处理数据](http://ideabook.phodal.com/#step-2-处理数据) * [文本转Logo](http://ideabook.phodal.com/#文本转logo) * [概况](http://ideabook.phodal.com/#概况-17) * [背景](http://ideabook.phodal.com/#背景-15) * [ShowCase](http://ideabook.phodal.com/#showcase-14) * [需求说明](http://ideabook.phodal.com/#需求说明) * [步骤](http://ideabook.phodal.com/#步骤-17) * [Step 1: Python 文字转Logo实战](http://ideabook.phodal.com/#step-1-python-文字转logo实战) * [GeoJSON与ElasticSearch实现高级图形搜索](http://ideabook.phodal.com/#geojson与elasticsearch实现高级图形搜索) * [概况](http://ideabook.phodal.com/#概况-18) * [Showcase](http://ideabook.phodal.com/#showcase-15) * [jQuery + Mustache + Leaflet](http://ideabook.phodal.com/#jquery-mustache-leaflet) * [步骤](http://ideabook.phodal.com/#步骤-18) * [Step 1: 离线地图与搜索](http://ideabook.phodal.com/#step-1-离线地图与搜索) * [Step 2: 从地点到地图上显示](http://ideabook.phodal.com/#step-2-从地点到地图上显示) * [Step 3: 从地图到地点上显示](http://ideabook.phodal.com/#step-3-从地图到地点上显示) License --- [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) [![Phodal's Idea](http://brand.phodal.com/shields/idea-small.svg)](http://ideas.phodal.com/) © 2016 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory. [![待我代码编成,娶你为妻可好](http://brand.phodal.com/slogan/slogan.svg)](http://www.xuntayizhan.com/person/ji-ke-ai-qing-zhi-er-shi-dai-wo-dai-ma-bian-cheng-qu-ni-wei-qi-ke-hao-wan/)

Documentation
3.8K Github Stars
migration
Open Source

migration

// 版本:v0.1.0 // 作者:Phodal Huang (PS 1:考虑到内容的重复度和读者一定会懂得 Google,对于部分的内容,我只编写标题。) (PS 2:部分内容可以在书上找到对应的内容,我只会给个书名) (PS 3:我在寒冷的北方杭州,写下了此指南) Phodal 手把手教你分析、评估现有系统、制定重构策略、探索可行重构方案、搭建测试防护网、进行系统架构重构、服务架构重构、模块重构、代码重构、数据库重构、重构后的架构守护……。 对就是这些,但是还没完呢。 指南中所包含的书单: - 《重构与模式》 - 《设计模式:可复用面向对象软件的基础》 - 《重构:改善既有代码的设计》 - 《领域驱动设计:软件核心复杂性应对之道》 - 《修改代码的艺术:构建易维护代码的 9 条最佳实践》 - 《代码整洁之道》 - 《架构整洁之道》 - 《数据库重构》 - 《遗留系统重构指南》 - 《软件架构师应该知道的97件事》 - 《架构师修炼之道》 - 《实现模式》 - 《反模式:危机中的软件,架构和项目的重构》 - 《精益软件度量》 - 《设计模式沉思录》 - 《前端架构:从入门到微前端》 # [系统重构与迁移指南](https://migration.ink/) > 软件开发总成本 = 开发成本 + 维护成本;软件维护成本 = 理解成本 + 修改成本 + 测试成本 + 部署成本。—— Kent Beck 最新版本内容,请访问:[https://migration.ink/](https://migration.ink/),或者 [https://github.com/phodal/migration](https://github.com/phodal/migration) 模型与原则: ![To DDD](images/to-ddd.png) 流程: ![重构流程](images/refactoring-process.png) 索引: ![Index](images/toc.jpg) 目录: * [再说遗留系统重构](#%E5%86%8D%E8%AF%B4%E9%81%97%E7%95%99%E7%B3%BB%E7%BB%9F%E9%87%8D%E6%9E%84) * [为什么重构?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%87%8D%E6%9E%84%EF%BC%9F) * [重构的动机](#%E9%87%8D%E6%9E%84%E7%9A%84%E5%8A%A8%E6%9C%BA) * [重构不一定能带来什么?](#%E9%87%8D%E6%9E%84%E4%B8%8D%E4%B8%80%E5%AE%9A%E8%83%BD%E5%B8%A6%E6%9D%A5%E4%BB%80%E4%B9%88%EF%BC%9F) * [重构是一种文化](#%E9%87%8D%E6%9E%84%E6%98%AF%E4%B8%80%E7%A7%8D%E6%96%87%E5%8C%96) * [大规模重构的时机](#%E5%A4%A7%E8%A7%84%E6%A8%A1%E9%87%8D%E6%9E%84%E7%9A%84%E6%97%B6%E6%9C%BA) * [说清重构的价值](#%E8%AF%B4%E6%B8%85%E9%87%8D%E6%9E%84%E7%9A%84%E4%BB%B7%E5%80%BC) * [系统重构模式与原则](#%E7%B3%BB%E7%BB%9F%E9%87%8D%E6%9E%84%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%8E%9F%E5%88%99) * [重构模式:EPDCA](#%E9%87%8D%E6%9E%84%E6%A8%A1%E5%BC%8F%EF%BC%9Aipdca) * [四级重构](#%E5%9B%9B%E7%BA%A7%E9%87%8D%E6%9E%84) * [小步前进](#%E5%B0%8F%E6%AD%A5%E5%89%8D%E8%BF%9B) * [Git 工作流](#git--%E5%B7%A5%E4%BD%9C%E6%B5%81) * [Master 机制](#master-%E6%9C%BA%E5%88%B6) * [PR 机制](#pr-%E6%9C%BA%E5%88%B6) * [建立远景与方向](#%E5%BB%BA%E7%AB%8B%E8%BF%9C%E6%99%AF%E4%B8%8E%E6%96%B9%E5%90%91) * [拉通:对齐目标](#%E6%8B%89%E9%80%9A%EF%BC%9A%E5%AF%B9%E9%BD%90%E7%9B%AE%E6%A0%87) * [明确潜在风险](#%E6%98%8E%E7%A1%AE%E6%BD%9C%E5%9C%A8%E9%A3%8E%E9%99%A9) * [人评估](#%E4%BA%BA%E8%AF%84%E4%BC%B0) * [重构范围](#%E9%87%8D%E6%9E%84%E8%8C%83%E5%9B%B4) * [产出物](#%E4%BA%A7%E5%87%BA%E7%89%A9) * [KPI 度量](#kpi-%E5%BA%A6%E9%87%8F) * [重建规范](#%E9%87%8D%E5%BB%BA%E8%A7%84%E8%8C%83) * [团队赋能](#%E5%9B%A2%E9%98%9F%E8%B5%8B%E8%83%BD) * [原则与模式](#%E5%8E%9F%E5%88%99%E4%B8%8E%E6%A8%A1%E5%BC%8F) * [重构评估与度量](#%E9%87%8D%E6%9E%84%E8%AF%84%E4%BC%B0%E4%B8%8E%E5%BA%A6%E9%87%8F) * [识别技术债务](#%E8%AF%86%E5%88%AB%E6%8A%80%E6%9C%AF%E5%80%BA%E5%8A%A1) * [技术债风暴](#%E6%8A%80%E6%9C%AF%E5%80%BA%E9%A3%8E%E6%9A%B4) * [架构评估:技术驱动 vs 业务驱动](#%E6%9E%B6%E6%9E%84%E8%AF%84%E4%BC%B0%EF%BC%9A%E6%8A%80%E6%9C%AF%E9%A9%B1%E5%8A%A8-vs-%E4%B8%9A%E5%8A%A1%E9%A9%B1%E5%8A%A8) * [代码评估:收集 bad smell](#%E4%BB%A3%E7%A0%81%E8%AF%84%E4%BC%B0%EF%BC%9A%E6%94%B6%E9%9B%86-bad-smell) * [收集 Todo](#%E6%94%B6%E9%9B%86-todo) * [测试和文档评估](#%E6%B5%8B%E8%AF%95%E5%92%8C%E6%96%87%E6%A1%A3%E8%AF%84%E4%BC%B0) * [项目评估](#%E9%A1%B9%E7%9B%AE%E8%AF%84%E4%BC%B0) * [编写工具评估](#%E7%BC%96%E5%86%99%E5%B7%A5%E5%85%B7%E8%AF%84%E4%BC%B0) * [代码评估工具](#%E4%BB%A3%E7%A0%81%E8%AF%84%E4%BC%B0%E5%B7%A5%E5%85%B7) * [真实的测试覆盖率](#%E7%9C%9F%E5%AE%9E%E7%9A%84%E6%B5%8B%E8%AF%95%E8%A6%86%E7%9B%96%E7%8E%87) * [可测试性评估](#%E5%8F%AF%E6%B5%8B%E8%AF%95%E6%80%A7%E8%AF%84%E4%BC%B0) * [度量](#%E5%BA%A6%E9%87%8F) * [寻找专业人士](#%E5%AF%BB%E6%89%BE%E4%B8%93%E4%B8%9A%E4%BA%BA%E5%A3%AB) * [重构准入条件](#%E9%87%8D%E6%9E%84%E5%87%86%E5%85%A5%E6%9D%A1%E4%BB%B6) * [工具准备](#%E5%B7%A5%E5%85%B7%E5%87%86%E5%A4%87) * [重构看板](#%E9%87%8D%E6%9E%84%E7%9C%8B%E6%9D%BF) * [版本控制](#%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6) * [自动化重构工具](#%E8%87%AA%E5%8A%A8%E5%8C%96%E9%87%8D%E6%9E%84%E5%B7%A5%E5%85%B7) * [持续集成环境](#%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%E7%8E%AF%E5%A2%83) * [记录](#%E8%AE%B0%E5%BD%95) * [探索性重构:可行方案](#%E6%8E%A2%E7%B4%A2%E6%80%A7%E9%87%8D%E6%9E%84%EF%BC%9A%E5%8F%AF%E8%A1%8C%E6%96%B9%E6%A1%88) * [准备知识:坏味道的模式](#%E5%87%86%E5%A4%87%E7%9F%A5%E8%AF%86%EF%BC%9A%E5%9D%8F%E5%91%B3%E9%81%93%E7%9A%84%E6%A8%A1%E5%BC%8F) * [C4 模型](#c4-%E6%A8%A1%E5%9E%8B) * [探索模式](#%E6%8E%A2%E7%B4%A2%E6%A8%A1%E5%BC%8F) * [1\. 从分层架构到具体代码](#1-%E4%BB%8E%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%88%B0%E5%85%B7%E4%BD%93%E4%BB%A3%E7%A0%81) * [2\. 从外部适配器到内部适配器](#2-%E4%BB%8E%E5%A4%96%E9%83%A8%E9%80%82%E9%85%8D%E5%99%A8%E5%88%B0%E5%86%85%E9%83%A8%E9%80%82%E9%85%8D%E5%99%A8) * [3\. 查看测试情况](#3-%E6%9F%A5%E7%9C%8B%E6%B5%8B%E8%AF%95%E6%83%85%E5%86%B5) * [4\. 针对于最复杂的情形和最简单的情形](#4-%E9%92%88%E5%AF%B9%E4%BA%8E%E6%9C%80%E5%A4%8D%E6%9D%82%E7%9A%84%E6%83%85%E5%BD%A2%E5%92%8C%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84%E6%83%85%E5%BD%A2) * [工具:API 列表和调用关系](#%E5%B7%A5%E5%85%B7%EF%BC%9Aapi-%E5%88%97%E8%A1%A8%E5%92%8C%E8%B0%83%E7%94%A8%E5%85%B3%E7%B3%BB) * [构建领域知识](#%E6%9E%84%E5%BB%BA%E9%A2%86%E5%9F%9F%E7%9F%A5%E8%AF%86) * [了解业务架构](#%E4%BA%86%E8%A7%A3%E4%B8%9A%E5%8A%A1%E6%9E%B6%E6%9E%84) * [架构全景](#%E6%9E%B6%E6%9E%84%E5%85%A8%E6%99%AF) * [领域名词表](#%E9%A2%86%E5%9F%9F%E5%90%8D%E8%AF%8D%E8%A1%A8) * [寻找高引用 + 高修改](#%E5%AF%BB%E6%89%BE%E9%AB%98%E5%BC%95%E7%94%A8--%E9%AB%98%E4%BF%AE%E6%94%B9) * [第一步:防护网](#%E7%AC%AC%E4%B8%80%E6%AD%A5%EF%BC%9A%E9%98%B2%E6%8A%A4%E7%BD%91) * [防护网策略](#%E9%98%B2%E6%8A%A4%E7%BD%91%E7%AD%96%E7%95%A5) * [第一个测试](#%E7%AC%AC%E4%B8%80%E4%B8%AA%E6%B5%8B%E8%AF%95) * [持续集成重构](#%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%E9%87%8D%E6%9E%84) * [检视测试](#%E6%A3%80%E8%A7%86%E6%B5%8B%E8%AF%95) * [系统架构重构](#%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E9%87%8D%E6%9E%84) * [高质量级 DDD 重构:演进驱动的事件风暴](#%E9%AB%98%E8%B4%A8%E9%87%8F%E7%BA%A7-ddd-%E9%87%8D%E6%9E%84%EF%BC%9A%E6%BC%94%E8%BF%9B%E9%A9%B1%E5%8A%A8%E7%9A%84%E4%BA%8B%E4%BB%B6%E9%A3%8E%E6%9A%B4) * [轻量级 DDD 重构:现状驱动的领域方案](#%E8%BD%BB%E9%87%8F%E7%BA%A7-ddd-%E9%87%8D%E6%9E%84%EF%BC%9A%E7%8E%B0%E7%8A%B6%E9%A9%B1%E5%8A%A8%E7%9A%84%E9%A2%86%E5%9F%9F%E6%96%B9%E6%A1%88) * [限界上下文要素](#%E9%99%90%E7%95%8C%E4%B8%8A%E4%B8%8B%E6%96%87%E8%A6%81%E7%B4%A0) * [微服务重构](#%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%87%8D%E6%9E%84) * [……](#%E2%80%A6%E2%80%A6) * [服务架构重构](#%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E9%87%8D%E6%9E%84) * [整洁架构](#%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84) * [设计新架构](#%E8%AE%BE%E8%AE%A1%E6%96%B0%E6%9E%B6%E6%9E%84) * [实施新分层架构](#%E5%AE%9E%E6%96%BD%E6%96%B0%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84) * [划分类,移动代码](#%E5%88%92%E5%88%86%E7%B1%BB%EF%BC%8C%E7%A7%BB%E5%8A%A8%E4%BB%A3%E7%A0%81) * [技术模块化 => 重搭](#%E6%8A%80%E6%9C%AF%E6%A8%A1%E5%9D%97%E5%8C%96--%E9%87%8D%E6%90%AD) * [中间态分层](#%E4%B8%AD%E9%97%B4%E6%80%81%E5%88%86%E5%B1%82) * [验收条件:构建](#%E9%AA%8C%E6%94%B6%E6%9D%A1%E4%BB%B6%EF%BC%9A%E6%9E%84%E5%BB%BA) * [潜在问题](#%E6%BD%9C%E5%9C%A8%E9%97%AE%E9%A2%98) * [bean 注入](#bean-%E6%B3%A8%E5%85%A5) * [解决冲突](#%E8%A7%A3%E5%86%B3%E5%86%B2%E7%AA%81) * [同步 API 修改](#%E5%90%8C%E6%AD%A5-api-%E4%BF%AE%E6%94%B9) * [对齐新分层架构](#%E5%AF%B9%E9%BD%90%E6%96%B0%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84) * [公共代码重构](#%E5%85%AC%E5%85%B1%E4%BB%A3%E7%A0%81%E9%87%8D%E6%9E%84) * [它真是个 util 吗?](#%E5%AE%83%E7%9C%9F%E6%98%AF%E4%B8%AA-util-%E5%90%97%EF%BC%9F) * [过度设计](#%E8%BF%87%E5%BA%A6%E8%AE%BE%E8%AE%A1) * [重新定义:消除二义性](#%E9%87%8D%E6%96%B0%E5%AE%9A%E4%B9%89%EF%BC%9A%E6%B6%88%E9%99%A4%E4%BA%8C%E4%B9%89%E6%80%A7) * [类进行内聚](#%E7%B1%BB%E8%BF%9B%E8%A1%8C%E5%86%85%E8%81%9A) * [划分技术部分](#%E5%88%92%E5%88%86%E6%8A%80%E6%9C%AF%E9%83%A8%E5%88%86) * [划分业务部分](#%E5%88%92%E5%88%86%E4%B8%9A%E5%8A%A1%E9%83%A8%E5%88%86) * [职责少 => 平级](#%E8%81%8C%E8%B4%A3%E5%B0%91--%E5%B9%B3%E7%BA%A7) * [业务代码多 => 再按业务拆分](#%E4%B8%9A%E5%8A%A1%E4%BB%A3%E7%A0%81%E5%A4%9A--%E5%86%8D%E6%8C%89%E4%B8%9A%E5%8A%A1%E6%8B%86%E5%88%86) * [模块重构](#%E6%A8%A1%E5%9D%97%E9%87%8D%E6%9E%84) * [组件聚合三原则](#%E7%BB%84%E4%BB%B6%E8%81%9A%E5%90%88%E4%B8%89%E5%8E%9F%E5%88%99) * [打破包之间的依赖关系](#%E6%89%93%E7%A0%B4%E5%8C%85%E4%B9%8B%E9%97%B4%E7%9A%84%E4%BE%9D%E8%B5%96%E5%85%B3%E7%B3%BB) * [依赖倒置](#%E4%BE%9D%E8%B5%96%E5%80%92%E7%BD%AE) * [更好的面向对象](#%E6%9B%B4%E5%A5%BD%E7%9A%84%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1) * [清理垃圾代码](#%E6%B8%85%E7%90%86%E5%9E%83%E5%9C%BE%E4%BB%A3%E7%A0%81) * [模型重构](#%E6%A8%A1%E5%9E%8B%E9%87%8D%E6%9E%84) * [聚合行为](#%E8%81%9A%E5%90%88%E8%A1%8C%E4%B8%BA) * [由内到外剥离,由外到内聚合](#%E7%94%B1%E5%86%85%E5%88%B0%E5%A4%96%E5%89%A5%E7%A6%BB%EF%BC%8C%E7%94%B1%E5%A4%96%E5%88%B0%E5%86%85%E8%81%9A%E5%90%88) * [识别模式 1:输入参数](#%E8%AF%86%E5%88%AB%E6%A8%A1%E5%BC%8F-1%EF%BC%9A%E8%BE%93%E5%85%A5%E5%8F%82%E6%95%B0) * [识别模式 2:返回参数](#%E8%AF%86%E5%88%AB%E6%A8%A1%E5%BC%8F-2%EF%BC%9A%E8%BF%94%E5%9B%9E%E5%8F%82%E6%95%B0) * [优化创建](#%E4%BC%98%E5%8C%96%E5%88%9B%E5%BB%BA) * [重命名:统一语言](#%E9%87%8D%E5%91%BD%E5%90%8D%EF%BC%9A%E7%BB%9F%E4%B8%80%E8%AF%AD%E8%A8%80) * [类](#%E7%B1%BB) * [状态](#%E7%8A%B6%E6%80%81) * [方法](#%E6%96%B9%E6%B3%95) * [容器](#%E5%AE%B9%E5%99%A8) * [离心分离模型:消除二义性](#%E7%A6%BB%E5%BF%83%E5%88%86%E7%A6%BB%E6%A8%A1%E5%9E%8B%EF%BC%9A%E6%B6%88%E9%99%A4%E4%BA%8C%E4%B9%89%E6%80%A7) * [提取参数对象](#%E6%8F%90%E5%8F%96%E5%8F%82%E6%95%B0%E5%AF%B9%E8%B1%A1) * [处理过程逻辑](#%E5%A4%84%E7%90%86%E8%BF%87%E7%A8%8B%E9%80%BB%E8%BE%91) * [领域特定语言](#%E9%A2%86%E5%9F%9F%E7%89%B9%E5%AE%9A%E8%AF%AD%E8%A8%80) * [模式重构](#%E6%A8%A1%E5%BC%8F%E9%87%8D%E6%9E%84) * [终止 Singleton](#%E7%BB%88%E6%AD%A2-singleton) * [工厂封装复杂构建](#%E5%B7%A5%E5%8E%82%E5%B0%81%E8%A3%85%E5%A4%8D%E6%9D%82%E6%9E%84%E5%BB%BA) * [重构手法](#%E9%87%8D%E6%9E%84%E6%89%8B%E6%B3%95) * [策略模式提供易变规则](#%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E6%8F%90%E4%BE%9B%E6%98%93%E5%8F%98%E8%A7%84%E5%88%99) * [建造者模式拆解复杂对象](#%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F%E6%8B%86%E8%A7%A3%E5%A4%8D%E6%9D%82%E5%AF%B9%E8%B1%A1) * [工具:自动化识别](#%E5%B7%A5%E5%85%B7%EF%BC%9A%E8%87%AA%E5%8A%A8%E5%8C%96%E8%AF%86%E5%88%AB) * [代码重构](#%E4%BB%A3%E7%A0%81%E9%87%8D%E6%9E%84) * [降低圈复杂度](#%E9%99%8D%E4%BD%8E%E5%9C%88%E5%A4%8D%E6%9D%82%E5%BA%A6) * [手法:提取](#%E6%89%8B%E6%B3%95%EF%BC%9A%E6%8F%90%E5%8F%96) * [手法:内联](#%E6%89%8B%E6%B3%95%EF%BC%9A%E5%86%85%E8%81%94) * [手法:移动](#%E6%89%8B%E6%B3%95%EF%BC%9A%E7%A7%BB%E5%8A%A8) * [方法移至其它类中](#%E6%96%B9%E6%B3%95%E7%A7%BB%E8%87%B3%E5%85%B6%E5%AE%83%E7%B1%BB%E4%B8%AD) * [标准:4 行的方法](#%E6%A0%87%E5%87%86%EF%BC%9A4-%E8%A1%8C%E7%9A%84%E6%96%B9%E6%B3%95) * [重构示例](#%E9%87%8D%E6%9E%84%E7%A4%BA%E4%BE%8B) * [评估](#%E8%AF%84%E4%BC%B0) * [C4 模型展开](#c4-%E6%A8%A1%E5%9E%8B%E5%B1%95%E5%BC%80) * [工具评估](#%E5%B7%A5%E5%85%B7%E8%AF%84%E4%BC%B0) * [代码统计](#%E4%BB%A3%E7%A0%81%E7%BB%9F%E8%AE%A1) * [基本情况评估](#%E5%9F%BA%E6%9C%AC%E6%83%85%E5%86%B5%E8%AF%84%E4%BC%B0) * [代码坏味道评估](#%E4%BB%A3%E7%A0%81%E5%9D%8F%E5%91%B3%E9%81%93%E8%AF%84%E4%BC%B0) * [架构评估](#%E6%9E%B6%E6%9E%84%E8%AF%84%E4%BC%B0) * [API 评估](#api-%E8%AF%84%E4%BC%B0) * [API 架构图](#api-%E6%9E%B6%E6%9E%84%E5%9B%BE) * [高引用 + 高修改分析](#%E9%AB%98%E5%BC%95%E7%94%A8--%E9%AB%98%E4%BF%AE%E6%94%B9%E5%88%86%E6%9E%90) * [测试](#%E6%B5%8B%E8%AF%95) * [重构策略](#%E9%87%8D%E6%9E%84%E7%AD%96%E7%95%A5) * [架构重构](#%E6%9E%B6%E6%9E%84%E9%87%8D%E6%9E%84) * [分层重构](#%E5%88%86%E5%B1%82%E9%87%8D%E6%9E%84) * [模型重构](#%E6%A8%A1%E5%9E%8B%E9%87%8D%E6%9E%84-1) * [代码重构](#%E4%BB%A3%E7%A0%81%E9%87%8D%E6%9E%84-1) * [自动化重构](#%E8%87%AA%E5%8A%A8%E5%8C%96%E9%87%8D%E6%9E%84) * [数据库重构(TBC)](#%E6%95%B0%E6%8D%AE%E5%BA%93%E9%87%8D%E6%9E%84%EF%BC%88tbc%EF%BC%89) * [数据库回归测试:数据不变性](#%E6%95%B0%E6%8D%AE%E5%BA%93%E5%9B%9E%E5%BD%92%E6%B5%8B%E8%AF%95%EF%BC%9A%E6%95%B0%E6%8D%AE%E4%B8%8D%E5%8F%98%E6%80%A7) * [前测试](#%E5%89%8D%E6%B5%8B%E8%AF%95) * [后测试](#%E5%90%8E%E6%B5%8B%E8%AF%95) * [引入数据库迁移工具](#%E5%BC%95%E5%85%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%81%E7%A7%BB%E5%B7%A5%E5%85%B7) * [迁移源数据](#%E8%BF%81%E7%A7%BB%E6%BA%90%E6%95%B0%E6%8D%AE) * [汲取式重构:存储过程转换](#%E6%B1%B2%E5%8F%96%E5%BC%8F%E9%87%8D%E6%9E%84%EF%BC%9A%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B%E8%BD%AC%E6%8D%A2) * [重构之后:工具的问题](#%E9%87%8D%E6%9E%84%E4%B9%8B%E5%90%8E%EF%BC%9A%E5%B7%A5%E5%85%B7%E7%9A%84%E9%97%AE%E9%A2%98) * [架构守护](#%E6%9E%B6%E6%9E%84%E5%AE%88%E6%8A%A4) * [架构守护测试](#%E6%9E%B6%E6%9E%84%E5%AE%88%E6%8A%A4%E6%B5%8B%E8%AF%95) * [坏味道守护](#%E5%9D%8F%E5%91%B3%E9%81%93%E5%AE%88%E6%8A%A4) * [测试驱动开发](#%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91) * [完善基础设施](#%E5%AE%8C%E5%96%84%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD) * [重构之后:解决人的问题](#%E9%87%8D%E6%9E%84%E4%B9%8B%E5%90%8E%EF%BC%9A%E8%A7%A3%E5%86%B3%E4%BA%BA%E7%9A%84%E9%97%AE%E9%A2%98) * [代码写得烂](#%E4%BB%A3%E7%A0%81%E5%86%99%E5%BE%97%E7%83%82) * [养成优秀的开发习惯](#%E5%85%BB%E6%88%90%E4%BC%98%E7%A7%80%E7%9A%84%E5%BC%80%E5%8F%91%E4%B9%A0%E6%83%AF) * [人员能力提升](#%E4%BA%BA%E5%91%98%E8%83%BD%E5%8A%9B%E6%8F%90%E5%8D%87) * [练习项目](#%E7%BB%83%E4%B9%A0%E9%A1%B9%E7%9B%AE) * [培训教程](#%E5%9F%B9%E8%AE%AD%E6%95%99%E7%A8%8B) * [典型问题](#%E5%85%B8%E5%9E%8B%E9%97%AE%E9%A2%98) * [重构之后:流程的问题](#%E9%87%8D%E6%9E%84%E4%B9%8B%E5%90%8E%EF%BC%9A%E6%B5%81%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98) * [流程优化](#%E6%B5%81%E7%A8%8B%E4%BC%98%E5%8C%96) * [需求规范化](#%E9%9C%80%E6%B1%82%E8%A7%84%E8%8C%83%E5%8C%96) * [实例化需求](#%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%9C%80%E6%B1%82) * [行为驱动开发(BDD)](#%E8%A1%8C%E4%B8%BA%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91%EF%BC%88bdd%EF%BC%89) * [重构之后:提炼知识](#%E9%87%8D%E6%9E%84%E4%B9%8B%E5%90%8E%EF%BC%9A%E6%8F%90%E7%82%BC%E7%9F%A5%E8%AF%86) * [知识仓库](#%E7%9F%A5%E8%AF%86%E4%BB%93%E5%BA%93) * [重建知识仓库](#%E9%87%8D%E5%BB%BA%E7%9F%A5%E8%AF%86%E4%BB%93%E5%BA%93) * [问题是什么?](#%E9%97%AE%E9%A2%98%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F) * [为什么我们需要重构?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E4%BB%AC%E9%9C%80%E8%A6%81%E9%87%8D%E6%9E%84%EF%BC%9F) * [重构到微服务](#%E9%87%8D%E6%9E%84%E5%88%B0%E5%BE%AE%E6%9C%8D%E5%8A%A1) * [未来](#%E6%9C%AA%E6%9D%A5) * [追求技术卓越。](#%E8%BF%BD%E6%B1%82%E6%8A%80%E6%9C%AF%E5%8D%93%E8%B6%8A%E3%80%82) * [推动个人变革,进而引领组织变革](#%E6%8E%A8%E5%8A%A8%E4%B8%AA%E4%BA%BA%E5%8F%98%E9%9D%A9%EF%BC%8C%E8%BF%9B%E8%80%8C%E5%BC%95%E9%A2%86%E7%BB%84%E7%BB%87%E5%8F%98%E9%9D%A9) * [整理知识,加强教育](#%E6%95%B4%E7%90%86%E7%9F%A5%E8%AF%86%EF%BC%8C%E5%8A%A0%E5%BC%BA%E6%95%99%E8%82%B2) * [在整个流程中将价值创造最大化](#%E5%9C%A8%E6%95%B4%E4%B8%AA%E6%B5%81%E7%A8%8B%E4%B8%AD%E5%B0%86%E4%BB%B7%E5%80%BC%E5%88%9B%E9%80%A0%E6%9C%80%E5%A4%A7%E5%8C%96) ## 再说遗留系统重构 > 当你试图对工作方式进行这些改进时,政治斗争可能抬起它丑陋的头——《拥抱变革:从优秀走向卓越的 48 个组织转型模式》 4 年前,也就是 2016 年,我一直在思索着如何更好的构建软件?如何更好的重写软件系统?思索出了 RePractise 七步曲,顺带着写了那本《全栈应用开发:精益实践》。书中,对于遗留系统的建议,便是**重写**。 而在我的最近一本书《[前端架构:从入门到微前端](https://book.douban.com/subject/33477112/)》中,我提及到了五种前端架构的改进方式:更新、迁移、重构、重写、重搭。重写,依旧是我推荐的主要方式,技术栈老旧、旧有代码不规范。 可是呢,随着岁月的流失,我发现重写并不能大部分的解决问题。 我总以为,编写软件的的人会随着年龄的增长,写出更好的软件系统。然而,软件开发者在经历到了 3 ~ 5 年的职业编码之后,有些成为了技术管理者,写不下去的转行、在线炒粉去了,还有的 `return true` 成为了销售……,剩下的,还有那个不断接锅的 Tech Lead(还在继续写代码)。就好像韭菜一样,总可以吃掉新鲜的,总会由新的人来开发新的系统。所以,《重构:改善既有代码的设计》总是能割到一波又一波的韭菜 —— 那个会重构的人,代码写得少了。 时过境迁,我对软件开发又有一些新的领悟:重构比重写更有挑战性。或许是重写和新写没有区别,或者是经历了一个个系统的重构过程,我大抵是明白了:**哪来的和旧系统划清界线**。系统腐烂时,没有人能说清整个系统,甚至于一半的功能都相当的困难。与此同时,或许系统的用户对系统的功能比你更加了解。因为,你会从他们那收到 bug 的反馈:以前不是有这个功能吗,非常好用 —— 用户可能会骗你,他/她经常用那个功能,但是那个功能是存在的。 从旧系统中汲取知识,一个逃离不了的话题,一个永远的痛。系统重构并不是一个简单的活,我们要不断地平衡:业务开发与重构过程,并尽量保证业务优先。它还涉及到一系列的软件开发实践: - 创建重构防护网,保证重构过程的安全性 - 可随时继续的重构演进策略 - 评估 - API 设计合理性评估 - 模块分层架构 - 架构合理度评估与对应的改进方案 - 公共代码的拆分策略 - 面向过程代码转面向对象 - 代码坏味道识别与代码重构 - 合适的设计模式替换旧的散弹式修改 - …… 也因此,在先前提到的 5 种方式中,重构可以说是最难的一种。设计新的架构很容易,但是要重构到设计模式,重构到领域驱动设计,重构到整洁的架构,并不是一件容易的事情。你需要持续不断地练习,但是这样的机会并不多。 可重构到最后,我们还会再回过头来看这些问题。我们的重点应该是:解决提出问题的人。正是那些能力不够的开发人员,导致了我们的系统需要一次大规模的重构。 那么,正确的做法,应该是在日常的开发中不断重构,并引入技术债看板,不断优化和解决这些技术债。故而,技术债管理优于重构。 ## 为什么重构? > 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。 从定义上来说,重构意味着,我们不能改变现有软件的行为,你的 bug 还应该是 bug。只是呢,我们可以顺带着修复这个 bug,但是它绝不能冠上重构的名义。如此一来,怕是会被人误解。 既然,不添加新的功能,也不减少现有的功能,那对于业务系统来说有什么价值呢? > 重构的唯一目的就是让我们开发更快,用更少的工作量创造更大的价值。—— 《重构:改善既有代码的设计》 所以,总的来说,重构是为了 `¥¥¥`。如果不能创造更多的价值,那么你在扯什么犊子的重构。但是,这种价值往往都不是直接体现。 ### 重构的动机 我知道你已经熟悉了这些说辞,而我也是复制过来的: - 改善既有代码的设计。副标题是最好的说明。 - 帮助找到软件中的 bug。review 旧有代码的过程中,会发现一些不符合业务的代码。 - 提升开发效率。让代码易于扩展 - 提高编程的趣味性——《重构与模式》 我也懒得打了。 ### 重构不一定能带来什么? > 重构的投入与回报并非呈线性关系。—— 《领域驱动设计:软件核心复杂性应对之道》 人们总以为重构能带来: - **更少的代码**。 - **更稳定的系统**。 - …… 它们都是**可能**带来的效果,而不是一定会。如: - 当我们拆分一个上帝类的时候,我们可能拆出了三个类、四个类,因为那些 import、package、type 的 declaration,反而带来了更多的代码。 - 而当我们发现重复代码,并且它们是可被抽象的,那么我们就可以消去一半的代码。 - 如果我们在重构的过程中,发现 bug,那么必然系统会更加稳定。 - 而如果因为我们重构,导致别人合并代码时发生冲突,反而可能会带来 bug。 - …… 我们是基于一系列的假设,才有了对应的结果描述。 而尽管我们达不成上述的好处,但我们仍然可以追求: - **编程的技艺**。稳定的业务代码,便容易选择合理的识别设计模式。 - **重构的手法**。是的,如果你持续练习的话。 - **持续重构的意识** 。我们真正意识到烂代码带来的问题。 - **模式和原则**。遇到相似的情况时,可以用相似的重构手法。 - **抽象能力**。后期总结,往往比前期更易于实践。 总的来说,它带来了更好的效果。 ### 重构是一种文化 日常的开发中,如果你说出你要重构的时候,有人对此挑战的话,那么说明需要培养相应的文化 —— 程序员的匠艺。好的代码不是一把写出来的,而是持续的演进和重构出来的 如果有开发人员,经常提出需要重构的时候,你要看看是否真的合理。 持续在在日常开发中进行小重构,应该是开发人员的一种**自我修养**。 ### 大规模重构的时机 对于大规模重构来说 ,一个最为常见的例子是:在接手别人或者别的系统的代码。觉得原有的代码不符合现有团队的风格,不满足再有的开发习惯。但是,根据我的观察,开发人员往往只是在改造系统,而非真正意义上的重构。 把握重构的时机是很难的一件事情,特别是如果你要进行大的系统改动,如果没有出现真正的痛点。那么,即使我们觉得诸多设计不合理的地方,我们往往可能并不被允许重构,也没有时间进行重构。可一旦连业务人员,也觉得系统需要重构的时候,那么系统也许需要重写了。 即便于对未来有价值,我们也仍需要等待更合适的时机,如:代码库拆分、整洁架构中的领域层拆分等等。我们都知道它们带来的好处,但是我们尚没有那么迫切。反而,因为拆分,我们可能会带来额外的重构成本和维护成本。如我之前在重构 [Coca](https://github.com/phodal/coca) 的时候,我在拆分 domain 层的时候,显得没有价值。我花了一两天的时间,把依赖于操作系统的部分,从代码中剥离了出去。我只是在试验 domain 层的好处,但是我并没有办法直接证明它的好处。 直到,我开始为 Coca 添加 WASM 支持的时候,编译成功了。并且,拆分之后,构建出来的 wasm 包的大小减少到原来的一半,因为有大量的依赖是我所不需要的。如果按正常的重构逻辑,应该是在我们在未来计划添加 WASM 的时候,架构师才允许我们拆分出来。 我们不应该做大规模预先的设计,也不应该过度的设计;我们也没有理由做大规模的预先重构设计,准备好方案,在时间恰当的时候,实施方案。 ### 说清重构的价值 重构,无法在短期内创造价值,哪怕是创造了大量的价值,它也难以**直接**体现出来。它可以提升代码的可读性,可维护性……,但是总的来说,它是在以特定的方式降低系统的维护成本。 再说说可维护性,比如,你使用了更好的设计模式代替了复制、粘贴,它可能还不如原来的代码生成器好用。开发的过程快了,维护就是一坨屎。所以,我说没有维护过旧系统的人,不懂得重构的好处。白天不懂得夜的美。 既然如此,让我们就再考虑一下重构的价值。我想了很久这个问题,直到我从《遗留系统重构指南》看到一些示例: | 阶段 | 描述 | 业务价值 | |------|-------------------------|----------------------| | 0 | 初步重构。定义组件接口,将组件拆分为独立的 jar | 清晰的接口能增加代码的可维护性 | | 1 | 重写身份认证。更改密码的存储方式 | 更好地遵守数据安全法规 | | 2 | 重写搜索组件。切换到不同的搜索引擎实现 | 搜索结果质量更好。用户更容易找到产品 | | 3 | 重构推荐组件。 | 可以快速地切换不同的算法 | 不过,仍旧存在大量的难以描述的价值——至少我是不知道从何说起。这里的代码、架构有坏味道,得去重构,便是我,一个工程师的思维。只是呢,真正有意义的业务价值,都是 TBC……。 尽管我们说,重构尽量不要影响业务开发,但是如果正在开发的功能部分,有较大的问题。那么,我们不得不调整策略,优先支持这部分的功能,而不是等完成后,再花费时间来对它们进行重构。 ## 系统重构模式与原则 > 设计模式强调为开发大规模系统提供可复用的设计指南。 —— 《反模式:危机中软件、架构和项目的重构》 就重构的基本原则来说,倒也不是很复杂: - 小步前进。走一小步,提交一次代码,方便回滚,有一天你会懂的。 - 随时可用。如果不能保证随时可用,那就说不上是重构了。 - 融入日常。 当你习惯了重构,记得在日常工作中使用。 ### 重构模式:EPDCA 我尝试从书中找到一个合适的模式,但是都没有发现符合我的步骤。便在 PDCA 的前面加了个 E,代表了 evaluate: 1. 识别需要重构的地方 2. 制定重构计划, 3. 执行计划的重构任务 4. 使用测试对重构是否影响业务功能进行检察 5. 调整下一次重构策略 对系统进行大规模重构的过程中,最难的地方在于识别,因为代码坏味道多的地方不一定是价值最高的。寻找你的价值曲线,寻找价值高、实施难度低的部分,是最体现你价值的地方。 ### 四级重构 实践的过程中,我们以拆解的方式,一步步由系统架构到代码级拆分。在某次吃饭的过程中,我发现不太对劲。我明明用的是敏捷式的重构方式,而非瀑布模式。它对应于四个不同的重构级别: - **架构重构**。在不改变业务逻辑的情况下,根据单一职责和依赖倒置原则的思想:对系统进行模块拆分与合并,以明确职责降低耦合度;对包进行重新规划,划分包之间的边界,减少代码间的耦合。 - **模型重构**。在包含测试的情况下,通过识别和发现模型的行为,将行为聚合到模型中:根据方法名称、参数、返回判定内聚到模型中;从流程梳理是否符合业务场景 。 - **模式重构**。对于特定代码坏味道产生的问题,通过结合架构模式、设计模式来提升可读性。如:使用工厂模式统一管理对象的创建;使用策略模式降低复杂度。 - **代码重构**。对于一些小的代码坏味道,可以通过 IDE 重构来快速改善即有代码,而不会影响到业务功能。如:复杂条件语句的提取;使用参数对象重构参数过多。 对应的模式如下图所示: ![四级重构](https://migration.ink/images/refactoring-levels.png) 这一点倒是与我们设计系统的时候,采用的《[架构金字塔](https://www.phodal.com/blog/architecture-pyramid/)》颇为一致的: ![架构金字塔](https://migration.ink/images/architecture-primard.jpg) ### 小步前进 **小步前进,拉一下最新的代码。** 不论改动的大小,一旦变动的文件多了,如移包、重命名用得广泛的类等等,记得随时提交。 多说无益,步子迈大的时候,你就会回到这句话上。 ### Git 工作流 如果你们使用的版本控制工具,还不是 Git 话,那么你们可能需要好好反思一下,为什么会到现在的这种地步? #### Master 机制 或许因为我合作的同事主要是 ThoughtWorks 的员工,所以在项目合作上,代码水平并不会太差;或许因为我能容忍那些年轻的开发人员犯的错。 我是一个喜欢用 master 分支的开发人员,主要是作为一个 Tech Lead,我并不想成为一个专职的 code reviewer。 所以,在 master 分支上重构,对于每个人都是一个极大的考验。有没有足够的测试覆盖?有没有足够的工程支持?有没有配合的团队合作? #### PR 机制 对于采用 pull request / merge request 机制的团队来说,重构并不会一帆风顺。 对于大的重构来说,如目录调整,你还能在花点时间重做。如果是代码重构,一旦重来的话,你可能会忘记你到底修改了什么。 也经常不得不找个夜深人静的时间,加会班,提交上代码。 所以,当你采用 PR 机制的时候,记得做一下笔记,写写你打算怎么改。 ## 建立远景与方向 TBD ### 拉通:对齐目标 会遇到不一样的需求,有的是明确的重构需求,有的则是隐藏在需求之后,有的则是看上去没有而已。 ### 明确潜在风险 你懂的。 ### 人评估 并非所有的人都具备足够的能力参与到重构的过程中。 所以,在我们进入重构之前,需要: - 确保对方有足够的能力 - 确保和对方对于重构有共同的看法 - 确保对方能配合你工作 为此,需要一些培训,又或者是激烈的讨论。 他/她们需要具备以下的基本技能: - 理解面向对象设计 - 了解设计模式 - 了解写测试的重要性 - 了解为什么要重构 - 追求代码质量 当然了,在了解的基本上有更深入的理解也是不错的。 ### 重构范围 对于一个大的系统来说,系统的每一部分并非都是等价的。 系统的核心就是系统的 core domain(核心域),一个有能力的管理者,能识别到哪一部分是系统的核心组成,并为它分配最好的开发人员;与此同时,对于支撑的部分来说,管理者只会分配少数的核心开发人员,只用于确保功能能按期完成。 按照 DDD 的思想来看,就是核心域、支撑域、通用域的区别。 ### 产出物 #### KPI 度量 #### 重建规范 #### 团队赋能 #### 原则与模式 ## 重构评估与度量 在接触代码之前,我们可以通过一些现成的工具,来对现有的项目进行一些不评估,并通过度量来提供指标。 ### 识别技术债务 > 对于技术债务,它的利息表现为系统的不稳定性,以及由于临时性手段和缺乏合适的设计、文档工作和测试带来的不断攀升的维护成本。 —— 《软件架构师应该知道的 97 件事》 如 Robert Nord 提出的 “技术债务全景图”(Tech Debt Landscape) 所示: ![技术债全景图](images/tech-debt-landscape.png) 技术债对于软件的影响:可维护性(Maintainability)、可演进性(Evolvability),而这些技术债对于非技术人员来说都是不可见的。它们源于生活,藏于黑暗中。 #### 技术债风暴 在重构开始之前,我们可以进行技术债的头脑风暴,收集每个开发人员每迫切解决的技术痛点。并按照优先级来评估这些技术债,列入我们的重构范围中。 如我的同事在《[技术债治理的四条原则](https://insights.thoughtworks.cn/managing-technical-debt/)》 一文中所介绍的,我们可以在对应的限界上下文里,可视化技术债: ![Classified Technical Debt Mapping](images/tech-debt-mapping.png) 再根据 “核心领域优于其他子域” 的原则,及其严重程度,来划分出技术债的优先级。 #### 架构评估:技术驱动 vs 业务驱动 如我在那篇 《[分层架构重构](https://www.phodal.com/blog/refactor-mvc-architecture-to-ddd/)》 中所说,在大量的现有系统中,我们发现了 MVC 架构模式被落地为三层分层架构(controller-service-model)。开发人员对它们的错误等同,导致了架构上的一系列错误。 对于简单的系统来说,CSM 的包结构问题不大。或者说,对于非常简单的系统来说,大泥球架构也没有问题。我们所针对的是那些中大规模的系统。在这些系统里,系统并非一次性的,开发出来就不再维护了。因此,它们需要对更合适的架构设计和包的拆分分。 借助于 [Tequila](https://github.com/newlee/tequila) 这样的架构可视化工具,又或者是 `coca arch`,便可以得到项目的调用关系图,它可以在某种层面上反应出系统的架构。根据它,我们可以知道: - 项目的结构划分是否合理 - 查看项目的代码中是否存在循环依赖的情况 结果如下图所示: ![Coca Call Graph](images/coca-call.png) 通过调用关系图,我们也可以查看类之间、包之间是否存在相互依赖。 #### 代码评估:收集 bad smell 对于这部分内容来说,你可以直接采用成熟的商业工具,如 SonarQube 便可以完成这方面的工作。 你也可以通过 `coca bs` 来做一些简单的 Bad Smell 收集: ```json { "dataClass": [ { "File": "examples/api/BookController.java", "BS": "dataClass" } ], "lazyElement": [ { "File": "examples/api/model/BookRepresentaion.java", "BS": "lazyElement" } ] } ``` 而后,再生成对应的重构建议。 #### 收集 Todo 代码中的 Todo 注释,是一些本应该发生的事情,本应该做好,但是我没有立即去做。换句话来说,Todo 都是项目中的技术债务,就了可能就永远不会做。 所以,我们需要有工具来查找项目的 Todo,如笔者编写的 Coca,可以寻找代码中的 Todo,包含其对应的日期、作者、提交信息、文件名及对其的行数等信息: | MESSAGES | FILENAME | LINE | |--------------------------------|--------------------------------------------------------------------------------------|------| happens on macosx, don't know why | .../ContributedLibraryTableCellJPanel.java | 118 Make this a method of Theme | .../ContributedLibraryTableCellJPanel.java | 233 Do a better job in refreshing only the needed element | .../LibraryManagerUI.java | 241 Do a better job in refreshing only the needed element | .../LibraryManagerUI.java | 273 Make this a method of Theme |.../MultiLibraryInstallDialog.java | 149 happens on macosx, don't know why | .../ContributedPlatformTableCellJPanel.java | 183 show error error when importing. ignoring :( | .../Base.java | 2423 Improve / move error handling |.../Editor.java | 1541 Should be a Theme value? |.../EditorHeader.java | 78 Should be a Theme value? |.../EditorStatus.java | 73 Improve decoupling | .../EditorTab.java | 465 随后,我们只需要根据真实的情况,更新项目中的 Todo,以确认出我们需要完成的技术债务。 不过,写好一个 Todo 并不是容易,万一以后大家都不写了呢? #### 测试和文档评估 关于测试的话题,我们会有一个大的专题来介绍相关的活动。 至于文档的缺乏,会在文中的最后介绍。 不过,你也可以参考我的那篇《[构建质量可信的软件系统](https://www.phodal.com/blog/build-trusted-software-system/)》 来对你的文档进行评估。 ### 项目评估 根据不同的项目,侧重点有所不同。 但是毫无疑问地,我们可以统计: - 功能的 bug 率,对应的 bug 修改时间 - bug 常见的问题 - …… 你都懂的。我暂时就不 copy 了。 ### 编写工具评估 在我遇到的一个重构项目中,项目中经常抛出Null Pointer Exception的问题。于是,我便写了一个简单的工具,来查找项目中返回Null Pointer Exception的代码,并对调用的地方进行评估。 随着评估的进一步深入,我在工具中加入了更多的功能,如: - 静态方法多,难以进行测试。要么是工具类过多,需要抽取基础设施;要么就是缺乏 OO 设计,导致过程性代码……。 - Util 过多,同上。 - Null Pointer Exception越多,则项目的出错可能性越多。 - 类方法数的标准差,能判断出对应的上帝类情况。 - 方法长度的标准差,越大则意味着方法的长度都比较长,方便于重构。 只需要运行 `coca evaluate`,就能得到以下的结果: | TYPE | COUNT | LEVEL | TOTAL | RATE | |--------------------------------|-------|-----------------------|-------|-----------| | Nullable / Return Null | 0 | Method | 1615 | 0.00% | | Utils | 7 | Class | 252 | 2.78% | | Static Method | 0 | Method | 1615 | 0.43% | | Average Method Num. | 1615 | Method/Class | 252 | 6.408730 | | Method Num. Std Dev / 标准差 | 1615 | Class | - | 7.344917 | | Average Method Length | 13654 | Without Getter/Setter | 1100 | 12.412727 | | Method Length Std Dev / 标准差 | 1615 | Method | - | 20.047092 | 笑,你只要加强使用 TDD,那么上述的大部分问题,都能得到进一步的缓解。 ### 代码评估工具 Java 世界流行的几个找问题工具: - FindBugs/SpotBugs - PMD/CPD - Checkstyle 试试你就知道了。 ### 真实的测试覆盖率 尽管有越来越多的项目将测试覆盖率作为一项考核指标。但是,对于诸多编程实践本身就好的公司为说,测试覆盖率也往往不是真的。 我们编写测试的其中一个目的是用于快速反馈,即当我们的功能出现问题的时候,我们可以快速通过测试来定位到问题所。然而,如果那些是没有断言的测试,那么我们就无法通过它来进行快速反馈。即,如果我们重构过程中,修改了某一块的功能,可能会进一步导致出现 bug。 为此,你可以借助于 Coca 的 Test Bad Smell 功能,来找到对应的问题。只需要执行 `coca tbs`,便能帮助你找到代码中的坏味道。它可以在你进入重构之前,帮你看看是否有对应的风险。 如下是 Coca 扫描出来的 Arduino 开源项目测试问题: | TYPE | FILENAME | LINE | |---------------------|---------------------------------------------------------------|------| | DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 107 | | DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 41 | | DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 63 | | RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 71 | | RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 72 | | RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 77 | | DuplicateAssertTest | app/test/cc/arduino/net/PACSupportMethodsTest.java | 19 | | DuplicateAssertTest | app/test/processing/app/macosx/SystemProfilerParserTest.java | 51 | | DuplicateAssertTest | app/test/processing/app/syntax/PdeKeywordsTest.java | 41 | | DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 57 | | DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 83 | | DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 109 | 好在上述的测试代码中,没有出现诸如于下面场景的测试坏味道: - EmptyTest。测试函数里空空如也 - UnknownTest。测试中没有对应的断言 - IgnoreTest。测试是被 Ingore 的,即不会运行的测试。 如果你的代码中出现了大量的上述问题,你需要好好反思一下,你的测试覆盖率是真实的吗? ### 可测试性评估 代码本身是缺乏测试的,那么它就是一个遗留系统。 ### 度量 根据《精益软件度量》对于度量的定义: - 度量在组织上下文中形成的一系列共识 - 将经验性模型转换为向量化模型(修改) - 包含人、流程、组织和工具的一个动态系统 TBD #### 度量缺陷 ### 寻找专业人士 你懂的。 ## 重构准入条件 看标题就够了。 ### 工具准备 ### 重构看板 ### 版本控制 ### 自动化重构工具 ### 持续集成环境 ### 记录 ## 探索性重构:可行方案 我喜欢重构的那种感觉 —— 把一坨烂代码,驯服成更易于阅读的代码。 ### 准备知识:坏味道的模式 软件开发是一种团队活动,当组织**缺乏强有力的**新鲜血液时,代码便会以相同的模式编写。该组织设计出来的架构,也从某种意义上出现了大量的趋同 —— 创新反而可能不好。在这种统一的模式之下,组织的代码也会呈现相似的坏味道。代码中的坏味道的模式也往往极为相似。 也因此,我们能快速地通过一小部分代码,了解整个项目的情况。 为此,我们只需要根据《重构:改善既有代码的设计》一书提出的 23 种代码坏味道,整理出系统中的常见坏味道。 | 集合类型 | 坏味道 | |---------|---------------| | 代码臃肿 | 过长函数,过大的类,基本类型偏执,过长参数列,数据泥团 | | 滥用面向对象 | Switch 声明,临时字段,被拒绝的遗赠,异曲同工的类 | | 变革的障碍 |发散式变化,霰弹式修改,平行继承体系 | | 非必要的 | 冗余类,纯稚的数据类,重复代码,夸夸其谈的未来性,注释 | | 耦合 | 不完美的类库,依恋情结,狎昵关系,消息链,中间人 | 随后,我们便能有针对性地对代码进行重构。 #### C4 模型 C4 Model 是一个非常不错的架构可视化工具,它从系统 System、容器 Container、组件 Component 和代码 Code 四个层次,由顶至底来介绍系统的架构: ![C4 模型](images/c4-model.jpg) 所以,我们可以根据 C4 模型的思路来查看整个应用的架构设计和代码情况。 ### 探索模式 #### 1. 从分层架构到具体代码 - 代码库间关系 - 代码库内模块化结构 - 模块化包结构 - 包内代码结构 #### 2. 从外部适配器到内部适配器 举个例子,从 API 入手: - Controller - Service - Model - DAO #### 3. 查看测试情况 - 是否包含单元测试 - 是否包含集成测试 - 测试覆盖率情况 - 测试编写情况 #### 4. 针对于最复杂的情形和最简单的情形 1. 从最简单的场景出发,对常规流程、包间关系有一定的了解 2. 从复杂的场景收尾,看最复杂的场景下会有什么问题 3. 根据需要寻找一个适合的场景, #### 工具:API 列表和调用关系 针对于那些使用 Spring 框架的后端项目来说,可以尝试使用 `coca api` 来生成项目 API 调用图。 ### 构建领域知识 作为一个碳基生物,我们要承认我们并非是全知的。我们所能做的事,来到一个新的领域时,能快速学习相关的知识。 #### 了解业务架构 > 高水平的软件架构师不仅要懂技术,还要掌握问题空间对应的业务领域知识。—— 《软件架构师应该知道的 97 件事》 #### 架构全景 简单来说,就是你知道对系统有一个初步的认知,它包含了:业务架构、技术架构、开发组成等等。 #### 领域名词表 根据开发人员介绍的各种概念,抽象出一份系统的领域名词表。它的来源是: - 项目的相关开发文档 - 项目中的架构资源 - 项目的代码 - …… 也因此,当你需要到一个新的项目实施重构计划时,一定需要有一个能与你结对编程的人。一个领域专家,一个代码专家,双方的无间配合,才能快速落地完成重构工作。 我们所要做的一个努力是,让文档、架构、元模型概念与代码实现一一对齐。这并不是一件容易的事情。从大多数项目的实践来看,架构师的设计和模型的实现往往是脱离的。因为设计架构的人和实现模型的人,往往不是同一些人。 我正在尝试根据它来编写这样的一个功能,但是它不是一件容易的事。只是呢,已经有一个小的雏形,通过 `coca concept` 从函数名称中抽取出部分的对应模式 | WORDS | COUNTS | |-------------|---------| | blog | 15 | | publish | 7 | | entity | 6 | | domain | 5 | | published | 5 | | resource | 4 | | criteria | 4 | 但是,这只能是构建领域名词表的一个 hello, world,并不足以承担起真正的领域名词活动,也无法构建真正的领域名词表体系。 除此,我还在寻找一种更高效的构建领域特定语言方式,它用于帮助我们高效地进行软件开发,这种社会学活动。 ### 寻找高引用 + 高修改 经常修改的代码,并不是见得是件好事。我们可以从中看到一些代码的坏味道,把它朝合适的方便引进,如类库,如包内聚等等 ![高引用 + 高修改](images/refs-change.png) 如果你使用的是 Git,可以通过 `coca git -t` 识别到高修改的文件: | ENTITYNAME | REVSCOUNT | AUTHORCOUNT | |---------------------------------------------------------------------------------------------------------------------|-----------|-------------| | build.gradle | 1326 | 36 | | src/asciidoc/index.adoc | 239 | 20 | | build-spring-framework/resources/changelog.txt | 187 | 10 | | spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java | 170 | 10 | | spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java | 159 | 15 | | src/docs/asciidoc/web/webmvc.adoc | 121 | 24 | | spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java | 118 | 9 | | src/dist/changelog.txt | 118 | 9 | | spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java | 116 | 15 | 上述的代码是 Spring Framework 中最常修改的文件,前面三个文件看上去是合理的,但是 ``AnnotationUtils.java`` 显然有问题。 对应的 ``DefaultListableBeanFactory.java`` 也有 2000+ 行左右的规模。 从代码的行数和修改次数来看,它们都是上帝类,并且经常出现 Bug。 下表是 Spring 源码中引用最多的方法: | REFS COUNT | METHOD | |------------|------------------------------------------------| | 2086 | org.springframework.util.Assert.notNull | | 952 | org.apache.commons.logging.Log.debug | | 773 | org.springframework.util.Assert.state | | 666 | org.apache.commons.logging.Log.isDebugEnabled | | 482 | org.apache.commons.logging.Log.trace | | 425 | org.springframework.util.StringUtils.hasText | | 398 | org.apache.commons.logging.LogFactory.getLog | | 392 | org.apache.commons.logging.Log.isTraceEnabled | | 268 | org.springframework.util.StringUtils.hasLength | | 213 | org.springframework.util.Assert.isTrue | 从代码上来看,还是 Util 方法被引用得最多。似乎我们仍然需要一个更可靠的算法,来保证被引用的正常度。 ## 第一步:防护网 尽管对于某些部分的重构来说,我们只是移动一下代码的位置 —— 如分层架构的调整,它不需要我们编写对应的测试。但是呢,出于流程完整性的考虑,这一步步往往流程比较长,毕竟它可以减少系统中 bug 的出现,降低重构的风险。与此同时,这是可以由团队一起协作完成的工作,特别适合于多人的协同重构方式。 ### 防护网策略 为了**保证对外暴露的 API 好的**,即我的重构不影响 API 的使用方,我们需要设计一个合适的防护策略。 在设计的时候,我们采用的是测试金字塔来帮助我们搭建测策略。我们会从下(单元测试)向上(集成测试)一步步搭建测试策略。 ![测试金字塔](images/test-primard.png) 而当我们重构的时候,我们则是自顶向下设计防护策略。常见的测试策略有 - 框架/模式库测试。xUnit,xMock,如 Java 语言里的 JUnit, Mockito;JavaScript 中的 Jest - 端到端 API 测试。JMeter,Postman,Rest Assured,Karate - UI 集成测试。Protractor 考虑到测试即文档,在实现实现的时候,会配合一些支持自然语言描述的框架,如: - 文档式测试,Gauge (主流语言),Concordion(Java) - BDD 测试,Cucumber(主流语言) - ATDD 测试,Robot Framework(Python 语言) 为了与运行客户端一配合,我们还需要有底层 API 来控制浏览器、客户端应用: - Appium。移动 APP 和桌面应用,支持主流语言 - Selenium。Web 浏览器,支持主流语言 - Puppeteer。Node.js API 操作 Chrome 浏览器 关于 APP 测试方案,可以参考我之前写的《[【架构拾集】移动应用的自动化测试(BDD 方式)](https://www.phodal.com/blog/phodal-architecture-101-mobile-appllication-test-architecture/)》 根据现有的 E2E(端到端)/集成测试框架的架构,我画了一个大致的测试策略分层架构图: ![测试架构](images/test-arch.png) 选择适合你们团队的测试架构,然后编写你的第一个测试。 ### 第一个测试 这个就简单了: 1. 选择方案,然后 Google 2. 寻找最简单的情形,编写测试 3. 只需要有了第一个,剩下的就是时间问题。 Done! ### 持续集成重构 如果你还没有持续集成环境的话,那么请搭建它。 考虑到这是一个体力活,而且这方面的资料已经足够的多,我就不浪费大家的时间了。 顺带一提,如果你的分支比较多,而且构建比较多,那么你可以考虑 `pipeline as pipeline` 的方式进行构建。 ### 检视测试 某次代码重构中,我发现代码的测试覆盖率很高,过程中出了一些错误,重构手法不正确是一个问题。但是在重构的过程中,发现有些测试都是没有意义的,所以这让我意思到在构建防护网的时候,有必要审视一遍测试,查找测试代码中的坏味道。 > 测试代码坏味道,是指单元测试代码中的不良编程实践(例如,测试用例的组织方式,实现方式以及彼此之间的交互方式),它们表明测试源代码中潜在的设计问题。 常见的测试坏味道有: - 空的测试。测试是生成的,但是没有内容。 - 忽略的测试。即测试被 Ignore - 没有断言的测试。为了测试覆盖率而出现的测试 - 多余的 Println。调试时留下的讯息。 - 多重断言。每个测试函数只应该测试一个概念。 - …… 有兴趣进一步了解的话,可以阅读《[测试代码的坏味道](https://www.phodal.com/blog/test-bad-smell/)》。 也可以 `coca tbs` 来查找测试中的坏味道: | TYPE | FILENAME | LINE | |---------------------|---------------------------------------------------------------|------| | DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 107 | | DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 41 | | DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 63 | | RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 71 | | RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 72 | | RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 77 | | DuplicateAssertTest | app/test/cc/arduino/net/PACSupportMethodsTest.java | 19 | | DuplicateAssertTest | app/test/processing/app/macosx/SystemProfilerParserTest.java | 51 | | DuplicateAssertTest | app/test/processing/app/syntax/PdeKeywordsTest.java | 41 | | DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 57 | | DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 83 | | DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 109 | ## 系统架构重构 > 架构将大问题分解为容易处理的小问题。——《架构师修炼之道 》 重量级 DDD 更多的是结合现状 + 未来出发,更多的人参与,提供准确的信息。 轻量级 DDD 则是通过现状,只需要少数熟悉的人,没有完成一致的达成,通过**持续性赋能**改善模型。 产品线 / 业务线来说,并非每一个子系统都是核心域,也不会投入大量的人和精力在上面。特别是,当你在重构的时候,投入的人员和精力不足以实施一个合理的事件风暴,以完成对应的领域驱动设计活动。所以,我们只能尽可能完善系统的设计。 ### 高质量级 DDD 重构:演进驱动的事件风暴 它意味着,重构时需要有: 1. 积极专注的团队参与 2. 复杂的场景 3. 领域专家参与协作 4. 迭代式的模型设计 为此需要: 1. 事件风暴 2. 识别上下文 3. 梳理上下文关系 4. 划分问题子域 5. 识别弹性边界 6. 领域建模 7. 分层架构 对应的我们需要: 1. 重搭架构 2. 编写集成测试 3. 搬移代码 4. …… 考虑到其复杂性,我建议采用《事件风暴设计工作坊》来完成相关的流程。 ### 轻量级 DDD 重构:现状驱动的领域方案 有意思的一个现象是,在真正落地的时候,对于核心的系统往往会抱着比较谨慎的态度 —— 除非原来的模式真的有问题。又或者是,所处的部门不是系统的核心部门时,在投入在会出现一定的问题。 看图: ![Quick ES](images/quick-ddd.png) 相应的步骤如下: 1. 结合工具识别所有的接口 2. 通过接口识别出领域名词(聚合、实体等) 3. 分析接口设计是否合理 4. 通过领域名词划分上下文边界 5. 重新定义领域名词 6. 结合领域名词划分新的 API 7. 划分领域和分层架构 8. 重新划分分层架构 9. 重构 service 代码,剥离领域逻辑 10. 重构代码到领域模型 对于未采用 Swagger 等 API 管理工具的项目,可以通过 `coca api` 生成 API 列表: ``` GET /aliyun/oss/policy POST /aliyun/osscallback GET /subject/list GET /esProduct/search GET /order/list GET /productAttribute/list/{cid} GET /productCategory/list/{parentId} GET /brand/list GET /esProduct/search/simple ... ``` ### 限界上下文要素 包含相关上下文的: - 实体(entity)。只要一个对象在生命周期中能够保持连续性,并且独立于它的属性(即使这些属性对系统用户非常重要),那它就是一个实体。它具有唯一标识和生命周期。 - 值对象(value object)。当你只关心某个对象的属性时,该对象便可作为一个值对象。它是实体的附加业务概念,用来描述实体所包含的业务信息。 - 领域服务(domain service)。封装了没有在模型中自然建模为值对象或实体的领域逻辑和概念。它的主要职责是使用实现和值对象编排业务逻辑。 - 领域事件(domain event)。它用于表明问题域中发生了一些业务人员关心的事情。在命名领域事件时,我们往往选择动词的过去分词,以明确表达事件的属性,其中文形式往往是『XXX已YYY』。 - 资源库(repository)。公开聚合根在内存中的集合的接口,提供聚合根的检索和持久化需要。 - 工厂(factory)。即在实体或者值对象创建复杂时,可以委托给工厂(模式)进行创建。。 - 聚合(aggregate)。是一种边界内的领域对象的集群,可以将其视为一个单元。可以封装一个到多个实体与值对象,用来维护该边界范围之内的业务完整性。 ### 微服务重构 一旦涉及到对微服务进行重构的时候,我们有很大的可能性要对系统进行 API 的重构设计。它也意味着,我们可能会影响到 API 的使用方,如果 API 也是自己内部的系统,那么问题就不是太大;如果 API 的使用方是第三方团队的系统时,我们就需要考虑一下兼容方案。这种兼容方案会在一定程度上影响到我们的重构,甚至于我们有可能不对做这方面的重构。 重构到微服务是一个巨大的挑战。 微服务是一个生态系统,它需要大量的基础设施进行配合,如部署管道、服务注册与发现、日志和监控、负载均衡等。 如果团队本身没有相关的经验,并且尝试往上调整的话,那么需要在心理上做好大量的准备。 如果你的目标是重构到微服务架构,那么我建议你先重构到 DDD + 整洁架构,它是微服务架构的一个中间态。即,先看看容器架构重构。 ### …… 待定。 由于笔者在这方面的经验不是非常丰富,但是我知道这里仍然有非常多的内容,可以探索。 欢迎大家来 pull request。 ## 服务架构重构 **小步前进,拉一下最新的代码。** 我在设计之初,想把标题改为容器架构重构,它对应上 C4 模型。纠结之后,我还是改为了服务架构。可见,命名之难。 ### 整洁架构 ![整洁架构](images/full-clean-architecture.png) Robert C. Martin 总结了六边形架构(即端口与适配器架构)、DCI (Data-Context-Interactions,数据-场景-交互)架构、BCI(Boundary Control Entity,Boundary Control Entity)架构等多种架构,归纳出了这些架构的基本特点: - 框架无关性。系统不依赖于框架中的某个函数,框架只是一个工具,**系统不能适应于框架**。 - 可被测试。业务逻辑脱离于 UI、数据库等外部元素进行测试。 - UI 无关性。不需要修改系统的其它部分,就可以变更 UI,诸如由 Web 界面替换成 CLI。 - 数据库无关性。业务逻辑与数据库之间需要进行解耦,我们可以随意切换 LocalStroage、IndexedDB、Web SQL。 - 外部机构(agency)无关性。系统的业务逻辑,不需要知道其它外部接口,诸如安全、调度、代理等。 如图所示 Clean Architecture 一共分为四个环,四个层级。环与环之间,存在一个依赖关系原则:**源代码中的依赖关系,必须只指向同心圆的内层,即由低层机制指向高级策略**。其类似于 SOLID 中的依赖倒置原则: - 高层模块不应该依赖低层模块,两者都应该依赖其抽象 - 抽象不应该依赖细节,细节应该依赖抽象 与此同时,四个环都存在各自核心的概念: - 实体 Entities (又称领域对象或业务对象,实体用于封装企业范围的业务规则) - 用例 Use Cases(交互器,用例是特定于应用的业务逻辑) - 接口适配器 Interface Adapters (接口适配器层的主要作用是转换数据) - 框架和驱动(Frameworks and Drivers),最外层由各种框架和工具组成,比如 Web 框架、数据库访问工具等 这个介绍可能有些简单,让我复制/粘贴一下更详细的解释: **实体(Entities)**,实体用于封装企业范围的业务规则。实体可以是拥有方法的对象,也可以是数据结构和函数的集合。如果没有企业,只是单个应用,那么实体就是应用里的业务对象。这些对象封装了最通用和高层的业务规则,极少会受到外部变化的影响。任何操作层面的改动都不会影响到这一层。 **用例(Use Cases)**,用例是特定于应用的业务逻辑,一般用来完成用户的某个操作。用例协调数据流向或者流出实体层,并且在此过程中通过执行实体的业务规则来达成用例的目标。用例层的改动不会影响到内部的实体层,同时也不会受外层的改动影响,比如数据库、UI 和框架的变动。只有而且应当应用的操作发生变化的时候,用例层的代码才随之修改。 **接口适配器(Interface Adapters)**。接口适配器层的主要作用是转换数据,数据从最适合内部用例层和实体层的结构转换成适合外层(比如数据持久化框架)的结构。反之,来自于外部服务的数据也会在这层转换为内层需要的结构。 **框架和驱动(Frameworks and Drivers)**。最外层由各种框架和工具组成,比如 Web 框架、数据库访问工具等。通常在这层不需要写太多代码,大多是一些用来跟内层通信的胶水代码。这一层包含了所有实现细节,把实现细节锁定在这一层能够减少它们的改动对整个系统造成的伤害。 ### 设计新架构 根据不同项目的实际情况,在真正落地的时候,会存在一些细微的差距。 如我的同事 [@huleTW](https://github.com/huleTW) 在 [django-ddd-demo](https://github.com/huleTW/django-ddd-demo) 项目中使用的 Python + Django 下的 DDD 分层架构如下所示: - **apis**:放各种 url 的 dispatcher - **application**:放各种 use case,use case 只能访问 domain 里面的 domain service - **domain**:放各个聚合,各个聚合里面有一个 domain service,domain service 操作可以聚合根,包含业务逻辑 - **infrastructure**:放读取数据(服务,中间件),以及公共的 util 包 我的同事 [@howiehu](https://github.com/howiehu) 在 [DDD Architecture Samples](https://github.com/howiehu/ddd-architecture-samples) 项目中使用了整洁架构分层如下: ``` ├── adapters 适配器 │   ├── inbound 入站适配器 │   └── outbound 出站适配器 ├── application 应用层 │   ├── concepts │   ├── dto │   ├── gateway │   └── usecases 用例 └── domain 领域层 ├── contexts 限界上下文 └── core 核心概念 ``` 因此对于整洁架构的外部适配器,人们通过有多种叫法 interfaces、apis、facades、presentation、adapters 等等, 而诸如 application、domain 和 infrastructure 来说,命名上倒是没有太大的区别。但是在实现上还是有相当大的区别: **粘合层(service)**。在 application 中的粘合层倒是有不同的叫法 service(application service)、usecases、interactors。但是在真正实施的时候,还会有巨大的差别,采用 service 可能会出现上帝类,于是就出现用例驱动的 xxxUsecase。 **瘦 application vs 胖 application**。采用 PresentationDomainDataLayering 架构,即瘦 application 层,会把用例放在 domain 中;而采用胖 application 架构,则会把 usecase 放在 application 中。两者的使用场景的区别,主要取决于 CRUD 的**纯净度**。如果你有 BFF,那么瘦 application 层适合于你;如果你是单体,又或者是跨实体操作多,那么胖 application 层适合你。 不过,这些都不重要,重要的是要不要出现尖叫架构 —— 一看就懂得业务: ![尖叫架构](images/screaming-architecture.png) 你只需要回答一个问题,你的场景复杂吗?复杂的话,你就拆 —— 将复杂问题繁杂化。 ### 实施新分层架构 实施新分层架构是一个持续的过程,它需要配合后续的各种重构模式。 #### 划分类,移动代码 这一步我们所做的是:移动旧的 service、controller、model 为竖直 + 水平架构。 对于大单体应用来说,从分层架构上,移到新的架构并不难:按各种层级创建应用,移动代码。 对于微服务架构应该来说,这一步也不算麻烦。 #### 技术模块化 => 重搭 对于原先模块划分不合理的应用来说,如: ``` ├── pom.xml ├── zheng-api-common │   └── pom.xml ├── zheng-api-rpc-api │   └── pom.xml ├── zheng-api-rpc-service │   └── pom.xml └── zheng-api-server └── pom.xml ``` 因为包、类之间本身是根据技术维度来划分的,如果我们计划以业务维度重新开发时,便就得创建新的目录结构,再移入新的类包。但是这样做的一个问题是,中间存在一个不可恢复的状态,会在一定程度上影响重构效果。 ### 中间态分层 考虑到服务重构的难度,它会存在着一个长期的中间态分层架构。原因有很多: - 无法短期内重构完 - 未拆到正确位置的 API - 领域层中的 repository 未拆分 所以,可以考虑将瘦 application 层作为中间态架构。 ### 验收条件:构建 是的,在这一步里,由于只是移动文件,所以只需要执行一下构建,你就能验证移动文件是否正确。 不过,由于有 IDEA 这样的工具上,想必不是问题。 然而,要是代码库过于庞大,那么我建议你试试 `coca refactor`。 ### 潜在问题 #### bean 注入 #### 解决冲突 #### 同步 API 修改 #### 对齐新分层架构 ## 公共代码重构 **小步前进,拉一下最新的代码。** > 创建通用的共享组件导致了一系列问题,比如耦合、协调难度和复杂度增加。 当我看到一个个巨大的 common 包时,我开始痛恨 `common`、`base`、`util` 这些该死的包,还有它们目录下统一管理的 `bean`。我们真的已经把它们用烂了,所以你应该重新审视一下你的项目代码。 所以,从这种意义上来说:**复用与低耦合**,本身存在一定的互斥关系。 公共代码往往缺乏抽象,又或者是追求过度的复用。 ### 它真是个 util 吗? > 哦,不,它是个恶魔,因为它是 util。 你会往 xxUtil 不加思索地扔入逻辑,正如你会往 common/bean 中扔入所有的 model,直次有一天,你拥有一个巨大无比的 base、common 代码。 大多数情况下,所有和业务相关的 Util 都存在一定的问题,如 CaptchaUtil,它要么应该划到自己的上下文中去,要么扔到诸如于 domain/shared 等共享上下文,而不是和其它 util 放到一起。 而诸如 FileUtil、DateUtil、RedisUtil、JdbcUtil 这些都可以说是基础设施相关的部分,它们可以划到 infrastructure/file 又或者是 infrastructure/date 目录下,而不是统一的管理这些 util。 如 [StackOverflow](https://stackoverflow.com/questions/1866794/naming-classes-how-to-avoid-calling-everything-a-whatevermanager) 的相关问题所列,我们还有诸如 Coordinator、Builder、Writer、Reader、Handler、Container、Protocol、Target、Converter、Controller、View、Factory、Entity、Bucket 等名称。 含义更加丰富的名字启示如下: **XXX器[拟物化]:** | Listener 监听器 | Adapter 适配器 | Filter 过滤器 | Iterator 迭代器 | Buffer 缓冲器 | Connector 连接器 | | ---------------------- | ---------------- | --------------------- | ------------------- | ------------------- | -------------------------- | | Decortor 装饰器 | Iterepter 解释器 | Interceptor 拦截器 | Reactor 反应器 | Configurator 配置器 | Wrapper 包装器 | | Proactor 主动器 | Monitor 监视器 | Controller 控制器 | Translator 转换器 | Acceptor 接收器 | Selector 选择器 | | Container 容器 | Manager 管理器 | Evictor 驱逐器 | Activator 激活器 | Mapper映射器 | Locator 定位器 | | Handler 处理器 | Assembler 汇编器 | Driver 驱动器 | Spliterator 分割器 | Builder 构建器 | Formatter 格式器 | | Scanner 扫描器 | Timer 定时器 | Converter 转化器 | Dispatcher 分配器 | Multicaster 广播器 | Transfer 传输器 | | Desriptor 描述器 | Encoder编码器 | Decoder 解码器 | Introspector 内省器 | Tokenizer 分词器 | Loader 加载器(ClassLoader) | | Logger 记录器 | Parser 解析器 | Resolver 分解器 | Incrementer 增加器 | Counter 计数器 | Collector 收集器 | | Initializer 初始化器 | Setter 设置器 | Getter 取值器 | Marshaller 编组器 | UnMarshaller 解组器 | Helper 帮助器 | | Accessor 访问器 | Visitor 访问器 | Reflector 反射器 | Embedder 嵌入器 | Finalizer 回收器 | Specifier 标识器 | | Supplier 供应器 | Processor 处理器 | Joiner 接合器 | Recorder 记录器 | Reducer 归集器 | Analyzer 分析器 | | Invoker 调用器 | Provider 供应器 | Renderer 渲染器 | Holder 持有器 | Closer 关闭器 | Operator 操作器 | | Appender 添加器 | Printer 打印器 | Tuplizer 元组器 | Caller 调用器 | Identifier 标识器 | Walker 漫步器 | | Brower 浏览器 | Server 服务器 | Aggregator 聚合器 | Binder 绑定器 | Validator 校验器 | Finder 查找器 | | Launcher 发射器/启动器 | Weaver 织入器 | Messenger 信差/消息器 | Extractor 提取器 | Sampler 取样器 | Profiler 优化器 | | Tracer 追踪器 | Estimator 预估器 | Generator 生成器 | Instrumenter 插装器 | Viewer 查看器 | Debugger 调试器 | | Analyser 分析器 | Inspector 检查器 | Linker 链接器 | Editor 编辑器 | Recognizer 识别器 | Decompiler 反编译器 | | Translator 解释器 | Lexer 词法分析器 | Tracker 追踪器 | Constructor 构造器 | Destructor 析构器 | Executor 执行器 | | Synchronizer 同步器 | Barrier 障碍器 | Allocator 分配器 | Bundler 打包器 | Applier 分发器 | Trigger 触发器 | **XXX者[拟人化]:** | Consumer消费者 | Producer 生产者 | Observer 观察者 | Caller 调用者 | Supervisor 监管者 | Keeper 管理员(ZooKeeper) | | -------------- | --------------- | --------------- | ------------- | ----------------- | ------------------------ | | Wokrer 工作者 | | | | | | 器和者的一些名字可以互换。比如Builder 可以是构建器,也可以是构建者。名字选择很多,但是不要过度封装,用最简单的概念表现更多的含义。 试着干掉 Utils ,你将收获更多的类,笑~。 Utils / Helper 多数是恶魔,无法满足单一职责和开闭原则。好的 OO 设计,大部分的类只表示一个事物,及其所有属性和操作。 1. 尽可能减少 Utils / Helper 类。好的 OO 设计,大部分的类只表示一个事物,及其所有属性和操作。 2. 如果使用一个 Utils 用于操作类,如 IList,那么它应该划到类中。除非该类不存在于当前的应用中。 3. Utils 中的方法应该是无状态的,比如没有 static 变量。 4. 如果有大量的 Utils 方法,应该把划分到类中,以便快速找到它们。 ### 过度设计 好的设计是尽可能简单的,它最易于适应新的设计,并能跟随业务的变化而变化。 - 开发人员:『这个功能是给未来准备的』 - Tech Lead:未来是多久?一个月后?半年后? - 开发人员:…… ![](images/balanced-design.png) 图片出自:https://stackoverflow.com/questions/1001120/what-is-over-engineering-as-applied-to-software 与之相对应的设计不足,则是因为经验的缘故。 ### 重新定义:消除二义性 当我们谈论 service 的时候,我们谈论的是同一个 service 吗? 当我们谈论 model 的时候,我们谈论的是同一种 model 吗? > 若对于一个文法的某一句子存在两棵不同的语法树,则该文法是二义性文法。 如果有多种不同类型的类,都被放置在 model 包下。那么,你应该消除 model 这个包,改为更表意的名称,如 Entity、Request、Response 等等。同理,一旦你们展开对某个名称的讨论时,是时候好好考虑其中的二义性。 最后,你还需要有一个相关领域的名词表。 避免产生二异性的词语。 ### 类进行内聚 参考下文中的模型重构。 ### 划分技术部分 如 Spring 框架的源码: ``` └── springframework ├── cache │ ├── annotation │ ├── concurrent │ ├── config │ ├── interceptor │ └── support ├── context │ ├── annotation │ ├── config │ ├── event │ ├── expression │ ├── i18n │ ├── index │ ├── support │ └── weaving ``` ### 划分业务部分 业务模块中的技术部分。 #### 职责少 => 平级 ``` └── orm ├── context ├── support ``` #### 业务代码多 => 再按业务拆分 ``` infrastructure └── repository ├── context │ ├── blog │ ├── advert │ └── pages ├── kafka ``` ## 模块重构 **小步前进,拉一下最新的代码。** > 模块/组件是软件的部署单元,是整个软件系统在部署过程中可以**独立部署**的最小实体。 —— 《架构整洁之道》 ### 组件聚合三原则 在我们设计软件工程的时候,我们 ![组件聚合](images/components-rules.png) Bob 大叔在书中提到了三个原则: - 复用/发布等同原则(REP)。软件复用的最小粒度等同于其发布的最小粒度。 - 共同闭包原则(CCP)。我们应该将那些会同时修改,并且为相同目的而修改的类放到同一个组件中,而将不会同时修改,并且不会为了相同目的而修改的那些类放到不同组件中。 - 共同复用原则(CRP)。不要强迫一个组件的用户依赖他们不需要的东西。 不过,其实按我的理解,第一条原则讲的是合理、有效的包发布策略;而后两条原则,只需要满足我们的模块/包满足开闭原则、单一职责,就可以合理地解决整个流程了。不过,这三个原则可以合理地解释在软件生命周期中,我们应该如何管理模块。 因此,对于自家的模块只需要:**根据技术、业务划分包,形成上下文边界,防止代码越界**。 ### 打破包之间的依赖关系 这一步理论上来看,倒也是蛮简单的: 1. 从 Gradle / Maven 找到想去除的依赖 2. 全局搜索依赖的包名 3. 解决依赖 - 提取到类库中 - 剥离并使用依赖注入 4. 删除依赖的包 5. 执行构建和 E2E 测试 TBC。工具还在写,目前主要要看人眼识别。 ### 依赖倒置 为此,我们可能需要寻找一些合适的依赖注入框架: - Java。Spring,Dagger 2,Guice 等等 - Go。Facebook Inject、Uber Dig、Google Wire 等 对了,静态语言呢? - 不需要。 #### 更好的面向对象 事实上,如果我们把面向对象做好的话,那么对应的逻辑就会封装到相应的对象中。 ### 清理垃圾代码 未使用的类,未使用方法。 它根本不知道什么时候会使用到,又或者是它已经通过多态来实现。 ## 模型重构 **小步前进,拉一下最新的代码。** > 架构元模型定义了模型中使用的概念和使用规则。 —— 《架构师修炼之道》 你可以将其对比于领域模型。 ### 聚合行为 对于领域模型来说,我们也无法直接在代码中实现架构元模型的所有概念。但是,我们所要做的事不断减少模型与代码之间的差异。如果我们不创建模式,而直接开始编写代码,那么我们会收获一堆上帝类。但是,反过来,当我们有一堆上帝类的时候,那么我们就需要从类中把行为都抽取出来。 当我们的贫血模型,拥有了行为,就可以进一步构成富血模型,符合面向对象(OO)的思想。进一步的,我们可以从业务的角度来考虑这个问题,将充血模型改为领域模型。 #### 由内到外剥离,由外到内聚合 对于那些已经采用 DDD 架构的项目来说,往往会遇到一些领域模型不完整、包含非领域相关代码等的情况。 遇到这种情况时,可以尝试: 1. 由内到外剥离非模型相关代码。只需要浏览一遍领域模型相关的代码,然后剥离不属于模型的代码;通过依赖注入、工厂模式等方式,建立整洁的 domain 层。 2. 由外到内聚合领域模型相关逻辑。这是一个复杂的过程,需要每个使用到模型的调用方,再看是属于领域相关的行为。 第一步可以在短期内快速实现,而第二步则需要一个漫长的过长 —— 取决于项目的大小。 #### 识别模式 1:输入参数 你懂的 #### 识别模式 2:返回参数 你懂的 #### 优化创建 笔者在某个重构项目中,遇到模型的创建逻辑很复杂 —— 参数多、场景多,所以做的第一件事情是:使用工厂模式优化了创建过程。 参考工厂模式。 ### 重命名:统一语言 在 DDD 中强调了统一语言的重要性,为此我们有必要对代码中的模型名称及其行为进行检视。在软件工程实践不好的团队中,你往往会出现**对于同一个事件,往往会有多种命名方式**。哪怕你觉得它是不正确的,因为 ownership 的缺乏,也没有人来统一对应的命名。 所以,在我们决定继续往下走之前,先学习一下怎么命名。 > 计算机科学只存在两个难题:缓存失效和命名。 —— Phil KarIton ![Arlo Belshee 命名的七步骤](images/7_stages_of_naming.png) 原文链接:[naming is a process](https://www.digdeeproots.com/articles/on/naming-as-a-process/) 但是还是更习惯于原来的文章中的: | 阶段 | 解释 | 示例 | |-----|------|------| | 空白 | 没有名称 | doSomething() | | 凑合 | 名称不能准确反应元素的含义| preload() | | 沾边 | 名称至少反映了元素某一方面的功能 | DomSomethingEvilToDB() | | 反映功能 | 名称直接描述了元素的所有功能 | ParseXmlAndStoreFightToDbAndLocalCacheAndStartProcessing() | | 反映角色 | 名称充分地反映了元素在架构中的角色 | StoreFightlightToDatabaseAndStartProcessing | | 反映意图 | 名称不仅反映元素的功能,还能反映其目的。 | BeginTrackingFlight() | | 领域抽象 | 名称超越了单个元素本身,成为一个新的抽象概念。 | MonitoringPanle.Add(new Flight()) | 偶然间,我看到我找到我书架上的《重构与模式》时,刚好看到一本《实现模式》,顺便看了看,发现书的内容对于本文有启发意义。 ![《实现模式》概览](images/implementation-patterns.png) 书中提及了四五种类型类、状态、消息与流(原行为)、方法,但是对于我们的统一语言工作来说,只需要重命名类、方法、状态就够了。 对应的解释如下: #### 类 > 数据的变化比逻辑要繁琐得多,正是这种现象让类有了存在的意义。—— 《实现模式》 对于继承的类来说,它应该遵循这么一些原则: - 超类名称要简单 - 子类名称要合格 #### 状态 状态包含了变量、字段、常量、局部参数、参数、参数对象等等。 #### 方法 > 在命名类和操作时要描述它们的效果和目的,而不要表露它们是通过何种方式达到目。 —— Eric Evans #### 容器 ### 离心分离模型:消除二义性 接下来就是处理剩下的 bean、model 等等模型。 在一个系统中,你会存在这么一些不同的 model: (PS:部分描述可能不准确,欢迎指正) * 与数据库表结构对应的 DO( Data Object)/ PO(Persistant Object)。 * 查询数据的 Query、Request。 * 对外传输的对象:DTO( Data Transfer Object)。 * 业务层之间的数据对象:VO(Value Object) / BO(Business Object)。 * 访问数据库的:DAO (Data Access Object数据访问对象)。 * 以及我们想要的 DDD 中的实体 Entity * 还有其它的 POJO( Plain Ordinary Java Object) 但是它们都是 model,所以它们都被扔到 model 中……,又或者是 bean 中……。导致,你有了一个巨大比的 model 层。 所以,在 DDD 又或者是 Clean Architecture,我们重新命名了不同的模式: * 使用 Command / Request 作为输入参数。其中的 Command 模式在完成后需要发出对应的 Event。 * 使用 Response / DTO / Representation 作为返回结果。 * 对 Entity 大家保持了一致的意见 * 还有 PO / DO 作为作为数据库的存储模型 * DAO 作为数据库的访问模型 * …… 不过,其实你只要不再让使用 model 和 bean,相信会有更多地收获。 ### 提取参数对象 如果一个类包含大量的参数,并且参数中存在一些相似的情形。对于概念统一的情况,可以提取成参数对象。 ### 处理过程逻辑 过程不应该模型的一部分,但是它是领域的一部分。 如 Eric Evans 在所说,区分是否显式表达概念的关键在于:过程是否经常被领域专家谈起,又或者只是计算机程序机制的一部分。 这时候,我们就需要规格(Specification)模式。 ### 领域特定语言 ## 模式重构 > 模式是某种场合下对某个问题的一个解决方案的**一种结构化展现**。 —— Jon Vlissides(GoF 成员)《设计模式沉思录》 **小步前进,拉一下最新的代码。** 不过,人们对于设计模式的看法就变成了砒霜,听上去有害,实际上真香。 我不是想鼓励使用设计模式,只是呢,如开头所说,我想展示的是如何用它来提升代码的可读性。也因此,我们的第一个例子是,干掉反模式的设计模式。 一旦你决定使用某种设计模式的时候,请确保**团队的所有人知道它解决了什么问题?** ### 终止 Singleton > 确保一个类只有一个实例,并提供一个全局访问点来访问该实例。—— 《设计模式:可复用面向对象软件的基础》 单例对象存活的时间通常很长,它们通常存在于程序的整个生命周期中。一个复杂应用可能有很多个单例,会使得上述问题更加严重。 整体来说它的缺点大过优点: 1. 对单例类的依赖被硬编码到其他类中,对具体类的依赖性破坏了OO。 2. 单例对接口不友好。 3. 单例getInstance没有继承性。 4. 多线程情况下有线程安全问题。 ### 工厂封装复杂构建 工厂是领域驱动设计中的重要组件。 工厂的目标: 1. 隐藏创建对象的复杂性 2. 减少对外暴露过多的内部结构 工厂模式是一种创建方法: 1. 工厂方法(factory method) 2. 抽象工厂(abstracty factory) 3. 建造者模式(builder) 值得注意的是:工厂并不总是需要独立的静态类。 尝试使用 `coca suggest` 会有惊喜。 #### 重构手法 对应的手法是: 1. 提取构建步骤到新的构建方法中 2. 将构建步骤方法转为 static 方法 3. 将这个 static 方法移到工厂类中 4. 将旧的构建函数提取成工厂方法 5. 将提取的工厂方法移到类中 6. 内联原有的工厂使用方 ### 策略模式提供易变规则 > 我们需要把过程中的易变部分提取到模型的一个单独的『策略』对象中,将规则与它控制的行为分开。——《领域驱动设计:软件核心复杂性应对之道》 看标题,你懂的。 ### 建造者模式拆解复杂对象 看标题,你懂的。 ### 工具:自动化识别 如 Coca 提供的 `coca suggest`功能,可以对相应的类提供一些建议,如: | CLASS | PATTERN | REASON | |--------|------------------|--------------------------------| | Insect | factory | too many constructor | | Bee | factory, builder | complex constructor, too many constructor, too many parameters | ## 代码重构 > 理想的方法长度不应该超过 4 行代码。—— Robert C Martin 常见的富血模型的类,往往包含了系统的行为。 常见贫血的原因是: 1. 缺乏抽象 2. 过程代码 3. 行为无法附着 4. 未考虑复用 从技巧上来说,主要是**提取、内联与移动**,配合上 IDE 的重构功能和《重构:改善既有代码的设计》,那么剩下的事件就简单了。 如果你缺乏这方面的技巧,欢迎与我们联系。 ### 降低圈复杂度 圈复杂度过大的代码,容易出问题。也因此呢,作为重构的第一步,只需要找一个工具,然后消除即可。 ### 手法:提取 提取分为: 1. 提取参数 2. 提取参数对象 3. 提取变量 4. 提取常量 5. 提取方法 ### 手法:内联 内联上的手法则是: 1. 内联参数 2. 内联变量 3. 内联常量 4. 内联方法 ### 手法:移动 移动的关键不在于 #### 方法移至其它类中 这里的 Ctrl + T 对应于老式的 IDEA 快捷键 Ctrl + Alt + Shift + T 1. 将函数转为静态方法,以解除对外的依赖(可选)(Static Method,Ctrl + T) 2. 将与函数职责无关的部分提取为函数的参数(Extract Functional Parameter,Ctrl + T) 3. 将函数的参数提取为参数对象(Extract Parameter Object,Ctrl + T) 4. 将函数移到参数对象中(Move Instance Method, F6) 完美。 ### 标准:4 行的方法 偶然间在《修改代码的艺术:构建易维护代码的 9 条最佳实践》上看到了那句:『理想的方法长度不应该超过 4 行代码』。 所以,我找出了书架上的那本《代码整洁之道》,找到了出处 32 页。 > 函数不该有 100 行那么长,20 行封顶最佳。 然后就是 Kent Beck 的 Java/Swing 小程序四行的故事。 但是,要保持一个好的习惯,寻找合适的机会去重构它。 ## 重构示例 我正在寻找一个合适的项目来演示如何重构。事实上,我已经找到了一些,但是它们的复杂度不太适合在线教学,所以我正在努力简化出一个可用的版本。 如果你已经迫不急待的话,可以尝试对以下的项目进行重构: - https://github.com/shuzheng/zheng - https://github.com/macrozheng/mall 它们是我在 GitHub 上找到的 Star 数相当多的开源 Java 项目,所以让我们来进行评估吧。 这两个项目的作者在项目中展示了极高的专业性和原则性。无论怎么说,这都是“好代码”,我也并非出于恶意的目的。如果你看了我在 GitHub 上的项目,你也会对我有诸多吐槽。我所尝试去做的是,以专业眼光来检视问题,不多也不少。作为一个追求匠艺的手工艺人,我们应该欢迎别人对我们这么做。只有批评才能让我们学到更多的东西。在进一步的重构之前,我们要再一次感谢两位作者将代码免费给社区的勇气和信心。他们做得相当的好。 相比之下,我大抵就只会复制和粘贴内容。 开始之前,你大可以从 GitHub 上复制这两个项目的其中一个,然后这里的例子以 mall 为例。你可以将 zheng 作为你的练手项目。 ### 评估 这两个项目都有丰富的文档,足够让你搭建好它们的环境。事实上,我觉得你可能不需要这样的操作,你需要打开你的 Intellij IDEA,然后构建一下,就可以阅读代码了。 #### C4 模型展开 首先,让我们以 C4 模型来展开这个项目的代码。 **展开系统目录结构**。从目录结构上来看,mall 系统的组织相当的不错,按业务和通用模块进行了拆分。 ``` ├── mall-common -- 工具类及通用代码 ├── mall-mbg -- MyBatisGenerator生成的数据库操作代码 ├── mall-security -- SpringSecurity封装公用模块 ├── mall-admin -- 后台商城管理系统接口 ├── mall-search -- 基于Elasticsearch的商品搜索系统 ├── mall-portal -- 前台商城系统接口 └── mall-demo -- 框架搭建时的测试代码 ``` **展开其中的一个微服务**。这里以 mall 为例,这是典型的 controller-service-dao 架构: ``` mall-admin ├── bo ├── component ├── config ├── controller ├── dao ├── dto ├── service └── validator ``` 而其中的 `home` 包,又以 **展开包架构**。接着,让我们看看 controller 包下的目录结构,这是按技术划分服务的架构模式: ``` controller ... ├── SmsFlashPromotionController.java ├── SmsFlashPromotionProductRelationController.java ├── SmsFlashPromotionSessionController.java ├── SmsHomeAdvertiseController.java ├── SmsHomeBrandController.java ├── SmsHomeNewProductController.java ├── SmsHomeRecommendProductController.java ├── SmsHomeRecommendSubjectController.java ├── UmsAdminController.java ├── UmsMemberLevelController.java ├── UmsPermissionController.java └── UmsRoleController.java ``` **展开类**。接着,让我们查看一下 OmsOrderReturnApplyController.java 文件,看看最后的接口: ```java @ApiOperation("修改申请状态") @RequestMapping(value = "/update/status/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult updateStatus(@PathVariable Long id, @RequestBody OmsUpdateStatusParam statusParam) { int count = returnApplyService.updateStatus(id, statusParam); if (count > 0) { return CommonResult.success(count); } return CommonResult.failed(); } ```` 用 OmsUpdateStatusParam 封装了请求参数,这个实践相当的不错。 接着,我们进入 `updateStatus` 方法看看: ```java @Override @Override public int updateStatus(Long id, OmsUpdateStatusParam statusParam) { Integer status = statusParam.getStatus(); OmsOrderReturnApply returnApply = new OmsOrderReturnApply(); if(status.equals(1)){ //确认退货 returnApply.setId(id); returnApply.setStatus(1); returnApply.setReturnAmount(statusParam.getReturnAmount()); returnApply.setCompanyAddressId(statusParam.getCompanyAddressId()); returnApply.setHandleTime(new Date()); returnApply.setHandleMan(statusParam.getHandleMan()); returnApply.setHandleNote(statusParam.getHandleNote()); }else if(status.equals(2)){ //完成退货 returnApply.setId(id); returnApply.setStatus(2); returnApply.setReceiveTime(new Date()); returnApply.setReceiveMan(statusParam.getReceiveMan()); returnApply.setReceiveNote(statusParam.getReceiveNote()); } ... ``` 嗯,这是一个典型的贫血模型设计,这些业务逻辑都可以内聚到领域模型中。 所以,我们有了初步的结论,可以尝试的内容: 1. 典型三层架构。可以业务维度重新做分层架构 2. 贫血模型。可以重构到充血模型 ### 工具评估 现在,是时候拿出我的 Coca:https://github.com/phodal/coca #### 代码统计 执行一下 `coca cloc` 可以看看项目的行数统计: ``` ─────────────────────────────────────────────────────────────────────────────── Language Files Lines Blanks Comments Code Complexity ─────────────────────────────────────────────────────────────────────────────── Java 471 80837 16265 2276 62296 1405 XML 112 21710 61 119 21530 0 YAML 12 430 36 8 386 0 Markdown 10 1211 271 0 940 0 JSON 8 1345664 0 0 1345664 0 gitignore 6 119 15 17 87 0 Shell 3 46 0 3 43 0 SVG 2 6132 0 988 5144 0 License 1 201 32 0 169 0 Properties File 1 4 0 0 4 0 SQL 1 2192 143 440 1609 0 ─────────────────────────────────────────────────────────────────────────────── Total 627 1458546 16823 3851 1437872 1405 ─────────────────────────────────────────────────────────────────────────────── Estimated Cost to Develop $55,872,945 Estimated Schedule Effort 70.766444 months Estimated People Required 93.525243 ─────────────────────────────────────────────────────────────────────────────── ``` #### 基本情况评估 先执行一下 `coca analysis`,然后 `coca evaluate`,得到一个基本的情况 | TYPE | COUNT | LEVEL | TOTAL | RATE | |--------------------------------|-------|-----------------------|-------|-----------| | Nullable / Return Null | 21 | Method | 13757 | 0.15% | | Utils | 2 | Class | 604 | 0.33% | | Static Method | 7 | Method | 13757 | 0.01% | | Average Method Num. | 13757 | Method/Class | 604 | 22.776490 | | Method Num. Std Dev / 标准差 | 13757 | Class | - | 52.137890 | | Average Method Length | 46177 | Without Getter/Setter | 11218 | 4.116331 | | Method Length Std Dev / 标准差 | 13757 | Method | - | 2.928149 | 从数据上看,静态方法只有 7 个,返回 null 的情况有 21 个,保持得不错。项目的平均方法长度也还行,就是平均方法有点多。 #### 代码坏味道评估 接着试试 `coca bs -s=type` 来查看代码中的常见坏味道,限于篇幅的原因这里就不复杂了,说主要问题:大的类一共有 72 个,其中 OmsOrderExample.java 类,在没有 set/get 的情况下有 576 个方法。查看了一下引用情况,好像是来看数据库查询用的……: ```java public Criteria andProductSnLike(String value) { addCriterion("product_sn like", value, "productSn"); return (Criteria) this; } ``` 这要重构的话是个体力活,详细见 [coca_reporter/bs.json](coca_reporter/bs.json) #### 架构评估 执行了一下 `coca arch`,由于是扁平的三层架构,没有太多的问题。 #### API 评估 执行 `coca api -c -r com.macro.mall.` 获得基本的 API 列表情况: | SIZE | METHOD | URI | CALLER | |------|--------|------------------------------------------------|--------------------------------------------------------------------| | 5 | GET | /prefrenceArea/listAll | controller.CmsPrefrenceAreaController.listAll | | 5 | GET | /subject/listAll | controller.CmsSubjectController.listAll | | 18 | GET | /subject/list | controller.CmsSubjectController.getList | | 28 | POST | /minio/upload | controller.MinioController.upload | | 13 | POST | /minio/delete | controller.MinioController.delete | | 5 | GET | /companyAddress/list | controller.OmsCompanyAddressController.list | | 18 | GET | /order/list | controller.OmsOrderController.list | | 11 | POST | /order/update/delivery | controller.OmsOrderController.delivery | | 11 | POST | /order/update/close | controller.OmsOrderController.close | | 11 | POST | /order/delete | controller.OmsOrderController.delete | 这里的 size 指的是调用的方法里,这里的 MinioController 的 upload 方法里一共调用了 28 个方法,打开一看: ```java @ApiOperation("文件上传") @RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public CommonResult upload(@RequestParam("file") MultipartFile file) { try { //创建一个MinIO的Java客户端 MinioClient minioClient = new MinioClient(ENDPOINT, ACCESS_KEY, SECRET_KEY); boolean isExist = minioClient.bucketExists(BUCKET_NAME); ... minioClient.putObject(BUCKET_NAME, objectName, file.getInputStream(), file.getContentType()); LOGGER.info("文件上传成功!"); MinioUploadDto minioUploadDto = new MinioUploadDto(); minioUploadDto.setName(filename); minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName); return CommonResult.success(minioUploadDto); } catch (Exception e) { LOGGER.info("上传发生错误: {}!", e.getMessage()); } return CommonResult.failed(); } ``` 嗯,它可能需要一个 service,而考虑到 Minio 是一个云存储服务器,还需要通过接口来封装这些细节。 #### API 架构图 在生成 API 结果之后,可以打开 `coca_reporter/arch.svg` 查看项目的架构图。不过,由于项目的 API 较多,便需要一个个分析,所以你可以通过 `coca api -c -r com.macro.mall. -a /order` 查看 /order 的所有接口情况: | SIZE | METHOD | URI | CALLER | |------|--------|-----------------------------|-----------------------------------------------------------------| | 17 | GET | /order/list | controller.OmsOrderController.list | | 11 | POST | /order/update/delivery | controller.OmsOrderController.delivery | | 11 | POST | /order/update/close | controller.OmsOrderController.close | | 11 | POST | /order/delete | controller.OmsOrderController.delete | | 5 | GET | /order/{id} | controller.OmsOrderController.detail | | 11 | POST | /order/update/receiverInfo | controller.OmsOrderController.updateReceiverInfo | | 11 | POST | /order/update/moneyInfo | controller.OmsOrderController.updateReceiverInfo | | 11 | POST | /order/update/note | controller.OmsOrderController.updateNote | | 5 | GET | /orderSetting/{id} | controller.OmsOrderSettingController.getItem | | 11 | POST | /orderSetting/update/{id} | controller.OmsOrderSettingController.update | | 5 | POST | /order/generateConfirmOrder | portal.controller.OmsPortalOrderController.generateConfirmOrder | | 2 | POST | /order/generateOrder | portal.controller.OmsPortalOrderController.generateOrder | | 2 | POST | /order/paySuccess | portal.controller.OmsPortalOrderController.paySuccess | | 2 | POST | /order/cancelTimeOutOrder | portal.controller.OmsPortalOrderController.cancelTimeOutOrder | | 5 | POST | /order/cancelOrder | portal.controller.OmsPortalOrderController.cancelOrder | 结果见下图(建议新窗口打开) ![Coca Reporter](coca_reporter/api.svg) #### 高引用 + 高修改分析 执行 `coca count` 可以查看高引用的方法: | REFS COUNT | METHOD | |------------|-----------------------------------------------------------------------| | 8055 | com.macro.mall.model.GeneratedCriteria.addCriterion | | 199 | com.macro.mall.common.api.CommonResult.success | | 125 | com.macro.mall.common.api.CommonResult.failed | | 30 | com.macro.mall.model.GeneratedCriteria.addCriterionForJDBCDate | | 23 | com.macro.mall.common.api.CommonPage.restPage | | 20 | com.macro.mall.model.GeneratedCriteria.addCriterionForJDBCTime | | 17 | com.macro.mall.portal.service.UmsMemberService.getCurrentMember | | 17 | com.macro.mall.model.UmsMember.getId | | 16 | com.macro.mall.service.impl.PmsProductServiceImpl.relateAndInsertList | | 7 | com.macro.mall.portal.domain.OrderParam.getUseIntegration | | 7 | com.macro.mall.portal.domain.OrderParam.getCouponId | 看上去,主要问题还在数据库查询语句拼接那一部分。 执行 `coca git -t` 可以查看经常修改的文件: | ENTITYNAME | REVSCOUNT | AUTHORCOUNT | |---------------------------------------------------------------------------------------------------------|-----------|-------------| | README.md | 121 | 2 | | document/pdm/mall.pdm | 29 | 2 | | document/pdm/mall.pdb | 26 | 2 | | mall-admin/pom.xml | 23 | 2 | | mall-portal/pom.xml | 18 | 2 | | document/sql/mall.sql | 16 | 2 | | mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java | 14 | 2 | | mall-admin/src/main/java/com/macro/mall/controller/PmsBrandController.java | 14 | 2 | | document/reference/deploy-windows.md | 14 | 2 | | mall-search/pom.xml | 14 | 2 | | document/docker/docker-deploy.md | 13 | 2 | 经常修改的地方是 pom 文件和文档,看上去没啥问题。考虑到 `OmsPortalOrderServiceImpl.java` 文件修改了 16 次,我决定打开这个文件看看: 1. 有一个 150 行左右的方法 2. 总行数 643 行 明显这是一个订单相关的上帝类,关联的 OmsOrder 模型有 40 ~ 50 左右的字段。毫无疑问,这里就是代码中经常出现问题的地方。 #### 测试 执行了 `coca tbs`,一共找到了这几个文件 ``` Start parse java call: PmsDaoTests.java Start parse java call: MallDemoApplicationTests.java Start parse java call: MallPortalApplicationTests.java Start parse java call: PortalProductDaoTests.java Start parse java call: MallSearchApplicationTests.java ``` 这个相当于是没有测试吧。 ### 重构策略 1. 进行 DDD 建模 2. 搭建 E2E 测试 3. 引入 Flyway 做数据库迁移 4. 分层架构重构 5. 重构到充血模型 6. …… ### 架构重构 以下内容暂不免费开放。 ### 分层重构 以下内容暂不免费开放。 ### 模型重构 以下内容暂不免费开放。 ### 代码重构 以下内容暂不免费开放。 ## 自动化重构 TBD ## 数据库重构(TBC) > 数据库重构,是对数据库 schema 的一个简单变更,在保持其行为语义和信息语义的同时,改进了它的设计。 —— 《数据库重构》 换言之,你既没有增加新功能,也没有破坏原有的功能,没有添加新的数据,也没有改变原有数据的语义。值得注意的是,尽管你的领域模型发生了变化,但是这也不意味着数据库 schema 需要因此而发生改变;与此同时,一些公司的制度也会限制我们对数据库进行重构。 它们之间的关系如下图所示: ![Modeling](images/modeling.jpg) 如《数据库重构》一书所说:数据库重构是一种数据库实现技术,就像代码重构是一种应用实现技术一样。当我们在不断对代码调优的同时,我们也在不断地对数据库进行调优。 ### 数据库回归测试:数据不变性 #### 前测试 采用传统的数据库测试工具,如 DBUnit,SQLUnit,通过诸如 TDD 的方式来完成对数据库的测试,随后开展对代码的重构。 #### 后测试 为了对重构进行验证,我们需要对数据库进行回归测试。一种常见的做法是: 1. 录制行为数据 2. 通过回复验证功能不被破坏 3. 通过影子流量进行验证 这样的工具有很多,如我司吴大师写的 [DBdiff](https://github.com/XuefengWu/dbdiff),可以用于 Oracle 数据库的迁移。 ### 引入数据库迁移工具 尽管,我并没有实践过大规模的数据库重构,但是和其他/她的后端开发一样,在日常的开发中,我们也时不时会做一些数据库重构 —— 数据库迁移。 - 代码版本控制 - 数据库版本控制 - 多数据识别 诸如于 Flyway 和 Liquibase 都是一些不错的工具。 ### 迁移源数据 我尝试编写脚本来将我的博客 [https://www.phodal.com](https://www.phodal.com) 从 SQLite3 迁移到 MySQL,这并不是一件困难的事情。因为 SQLite3 是一种嵌入式数据库,而我们可以通过 `mysqldump` 出 MySQL 的数据。 如此一来,我们便可以用于做数据库重构练习,并编写数据库的回归测试。 ### 汲取式重构:存储过程转换 对于采用 Oracle 的项目来说,数据重构不是一件容易的事情。它还涉及到一系列的存储过程代码。 所以,我们还可以尝试将 PLSQL 转为 Kotlin 代码:https://github.com/XuefengWu/LADE_Analysis ## 重构之后:工具的问题 ### 架构守护 在重构的过程中,我们也可以尝试将它引入到系统中使用。它可以作为我们的架构适应度函数,不断地见证我们架构的变化和成长 —— 以一种肉眼的方式持续演进。 #### 架构守护测试 ArchUnit 是一个不错的架构守护框架。你可以尝试引入项目的架构中, #### 坏味道守护 我的意思是,只需要集成到持续构建中即可。 ### 测试驱动开发 测试驱动开发适合于解决复杂的问题。 我并不是一个 TDD 的死忠粉,但是我发现 TDD 非常适合于解决复杂问题。在这个场景下,我有一堆事先准备好的 Test Bad Smell,它们拥有一些对应的检测规则,这些规则相互之间存在一些影响。因此,我将这些 Bad Smell 作为我的测试用例: ``` AssertionRouletteTest.java ConditionalTest.java ConstructorInitializationTest.java DuplicateAssertTest.java EmptyTest.java IgnoreTest.java MagicNumberTest.java MysteryGuestTest.java RedundantAssertionTest.java ``` 于是,我可以写出我的第一个测试: ```java func TestTbsApp_EmptyTest(t *testing.T) { g := NewGomegaWithT(t) var codePath = "../../../_fixtures/tbs/code/EmptyTest.java" var result = buildTbsResult(codePath) g.Expect(result[0].Type).To(Equal("EmptyTest")) } ``` 对应的一步步完成出整个功能的代码。而后,我们就可以重构出理想的代码 —— 因为我们已经有了对应的测试。 ### 完善基础设施 ## 重构之后:解决人的问题 我们把所有的问题都归到人的问题。 ### 代码写得烂 如我们所见,我们在不同的公司里,会看到不同的人写出来的代码水平是不一样的。比如有一些是由供应商写的代码 不过,既然给这引起供应商的工资本身就不高,那么怎么能指望出他/她写出好的代码。 与此同时,由于种种原因,教他们学习的成本又特别高,甚至于你还要担心如果你教会了供应商,他/她们可能寻找一个新的工作。 ### 养成优秀的开发习惯 ### 人员能力提升 #### 练习项目 #### 培训教程 #### 典型问题 ## 重构之后:流程的问题 ### 流程优化 我们在开发的前期缺少对于系统的设计,那么我们将需要花费大量的成本在后期修复他们。 你可以查看我先前写的《[新项目检查清单](https://www.phodal.com/blog/new-project-check-lists/)》完善项目的启动流程,通过 [https://phodal.github.io/new-project-checklist/](https://phodal.github.io/new-project-checklist/) 可以使用这个工具。并借助于《[如何创建你的应用脚手架](https://www.phodal.com/blog/how-to-create-application-boilerplate/)》搭建好项目的脚手架。 借助于 Path to Production [优化你的上线流程](https://www.phodal.com/blog/tech-lead-tools-path-to-production/) ![Path to Production](images/path-to-production.png) 工具见:[https://phodal.github.io/path/](https://phodal.github.io/path/) 你可以在我的 Tech Lead 工具箱中,获得更多工具:https://github.com/phodal/techlead ### 需求规范化 #### 实例化需求 #### 行为驱动开发(BDD) > 行为驱动开发是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA 和非技术人员或商业参与者之间的协作。 年轻的时候(我刚毕业),我并不懂得 BDD 的好处——相当的繁琐,到了现在我又有一番不一样的体会。思来想去主要的原因是,当时我们是一个全功能的团队,不存在跨部门协作,也因此我并不觉得 BDD 能为我们带来多大的好处。 可是在 ThoughtWorks 公司之外,开发、测试和业务都是各自的部门,有着各自的利益。通过 BDD 的协作方式,可以让交付物有一个一致的观点。 ## 重构之后:提炼知识 ### 知识仓库 预先设计: - 一无是处的代码注释。 - 没有搭建信息的 README。 - 没有版本化的、二进制的项目文档 ### 重建知识仓库 请参考:[构建可信软件系统](https://github.com/phodal/trusted) ## 问题是什么? ### 为什么我们需要重构? 这个问题,就该你回答了:为什么我们需要重构? ## 重构到微服务 重构比重写来得更有价值。重写往往有可能带来更多的技术债,还可能犯上同样的错误。 你为什么不重写呢??? 这样更省时间? ## 未来 未来 10 年,你应当: ### 追求技术卓越。 ### 推动个人变革,进而引领组织变革 ### 整理知识,加强教育 ### 在整个流程中将价值创造最大化 LICENSE --- [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) © 2020 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 4.0 License. See `LICENSE` in this directory. [![待我代码编成,娶你为妻可好](http://brand.phodal.com/slogan/slogan.svg)](http://www.xuntayizhan.com/person/ji-ke-ai-qing-zhi-er-shi-dai-wo-dai-ma-bian-cheng-qu-ni-wei-qi-ke-hao-wan/)

E-book Management Documentation
3.5K Github Stars
growth-in-action
Open Source

growth-in-action

[全栈增长工程师](https://github.com/phodal/growth-ebook)实战 === 在Growth中我们介绍的只是一系列的实践,而Growth实战则会带领读者去履行这些实践。你将会看到: - 如何开发一个Web应用(博客) - 如何编写测试——单元测试、功能测试、自动化UI测试 - 搭建并使用持续集成 - 添加SEO支持——Sitemap、站长工具和Google Analytics - 创建API,制作AutoComplete - 开发相应的APP及其API——查看文章、用户登录、发表文章 - 制作单页面应用 - 可配置管理 **阅读过程中遇到任何问题,请以issue的形式提出来,这样可以帮助其他读者来发现、解决这个问题。** 在线阅读:[http://growth-in-action.phodal.com/](http://growth-in-action.phodal.com/) 下载地址: [https://github.com/phodal/growth-in-action/releases](https://github.com/phodal/growth-in-action/releases) Web应用代码: [https://github.com/phodal/growth-in-action-python-code](https://github.com/phodal/growth-in-action-python-code) APP代码: [https://github.com/phodal/growth-in-action-hybird](https://github.com/phodal/growth-in-action-hybird) 欢迎关注我的微信公众号(搜索Phodal): ![QRCode](http://articles.phodal.com/qrcode.jpg) 目录 --- * [序:如何成为全栈增长工程师?](http://growth-in-action.phodal.com/#序如何成为全栈增长工程师) * [先成为全栈工程师](http://growth-in-action.phodal.com/#先成为全栈工程师) * [再成为增长工程师](http://growth-in-action.phodal.com/#再成为增长工程师) * [全栈增长工程师实战](http://growth-in-action.phodal.com/#全栈增长工程师实战) * [准备工作和工具](http://growth-in-action.phodal.com/#准备工作和工具) * [深入浅出Django](http://growth-in-action.phodal.com/#深入浅出django) * [Django简介](http://growth-in-action.phodal.com/#django简介) * [Django应用架构](http://growth-in-action.phodal.com/#django应用架构) * [Django hello,world](http://growth-in-action.phodal.com/#django-helloworld) * [安装Django](http://growth-in-action.phodal.com/#安装django) * [创建项目](http://growth-in-action.phodal.com/#创建项目) * [Django后台](http://growth-in-action.phodal.com/#django后台) * [第一次提交](http://growth-in-action.phodal.com/#第一次提交) * [三步创建博客应用](http://growth-in-action.phodal.com/#三步创建博客应用) * [Tasking](http://growth-in-action.phodal.com/#tasking) * [创建BlogpostAPP](http://growth-in-action.phodal.com/#创建blogpostapp) * [生成APP](http://growth-in-action.phodal.com/#生成app) * [创建Model](http://growth-in-action.phodal.com/#创建model) * [配置URL](http://growth-in-action.phodal.com/#配置url) * [创建View](http://growth-in-action.phodal.com/#创建view) * [创建博客列表页](http://growth-in-action.phodal.com/#创建博客列表页) * [创建博客详情页](http://growth-in-action.phodal.com/#创建博客详情页) * [测试](http://growth-in-action.phodal.com/#测试) * [测试首页](http://growth-in-action.phodal.com/#测试首页) * [测试详情页](http://growth-in-action.phodal.com/#测试详情页) * [自动化测试与持续集成](http://growth-in-action.phodal.com/#自动化测试与持续集成) * [编写自动化测试](http://growth-in-action.phodal.com/#编写自动化测试) * [Selenium与第一个UI测试](http://growth-in-action.phodal.com/#selenium与第一个ui测试) * [搭建持续集成](http://growth-in-action.phodal.com/#搭建持续集成) * [Jenkins创建任务](http://growth-in-action.phodal.com/#jenkins创建任务) * [创建shell](http://growth-in-action.phodal.com/#创建shell) * [更完善的博客系统](http://growth-in-action.phodal.com/#更完善的博客系统) * [静态页面](http://growth-in-action.phodal.com/#静态页面) * [安装 flatpages](http://growth-in-action.phodal.com/#安装-flatpages) * [创建模板](http://growth-in-action.phodal.com/#创建模板) * [评论功能](http://growth-in-action.phodal.com/#评论功能) * [Sitemap](http://growth-in-action.phodal.com/#sitemap) * [站点地图介绍](http://growth-in-action.phodal.com/#站点地图介绍) * [创建首页的Sitemap](http://growth-in-action.phodal.com/#创建首页的sitemap) * [创建静态页面的Sitemap](http://growth-in-action.phodal.com/#创建静态页面的sitemap) * [创建博客的Sitemap](http://growth-in-action.phodal.com/#创建博客的sitemap) * [提交到搜索引擎](http://growth-in-action.phodal.com/#提交到搜索引擎) * [样式与UI美化](http://growth-in-action.phodal.com/#样式与ui美化) * [响应式设计](http://growth-in-action.phodal.com/#响应式设计) * [引入前端框架](http://growth-in-action.phodal.com/#引入前端框架) * [页面美化](http://growth-in-action.phodal.com/#页面美化) * [添加导航](http://growth-in-action.phodal.com/#添加导航) * [添加标语](http://growth-in-action.phodal.com/#添加标语) * [优化列表](http://growth-in-action.phodal.com/#优化列表) * [添加footer](http://growth-in-action.phodal.com/#添加footer) * [应用API](http://growth-in-action.phodal.com/#应用api) * [博客列表](http://growth-in-action.phodal.com/#博客列表) * [Django REST Framework](http://growth-in-action.phodal.com/#django-rest-framework) * [创建博客列表API](http://growth-in-action.phodal.com/#创建博客列表api) * [测试 API](http://growth-in-action.phodal.com/#测试-api) * [自动完成](http://growth-in-action.phodal.com/#自动完成) * [搜索API](http://growth-in-action.phodal.com/#搜索api) * [页面实现](http://growth-in-action.phodal.com/#页面实现) * [跨域支持](http://growth-in-action.phodal.com/#跨域支持) * [添加跨域支持](http://growth-in-action.phodal.com/#添加跨域支持) * [创建移动应用](http://growth-in-action.phodal.com/#创建移动应用) * [hello,world](http://growth-in-action.phodal.com/#helloworld) * [构建应用](http://growth-in-action.phodal.com/#构建应用) * [博客列表页](http://growth-in-action.phodal.com/#博客列表页) * [列表页](http://growth-in-action.phodal.com/#列表页) * [详情页](http://growth-in-action.phodal.com/#详情页) * [Profile](http://growth-in-action.phodal.com/#profile) * [Json Web Tokens](http://growth-in-action.phodal.com/#json-web-tokens) * [登录表单](http://growth-in-action.phodal.com/#登录表单) * [Profile](http://growth-in-action.phodal.com/#profile-1) * [创建博客](http://growth-in-action.phodal.com/#创建博客) * [移动单页面应用](http://growth-in-action.phodal.com/#移动单页面应用) * [移动设备处理](http://growth-in-action.phodal.com/#移动设备处理) * [前后端分离](http://growth-in-action.phodal.com/#前后端分离) * [Riot.js](http://growth-in-action.phodal.com/#riot.js) * [ReactiveJS构建服务](http://growth-in-action.phodal.com/#reactivejs构建服务) * [创建博客列表页](http://growth-in-action.phodal.com/#创建博客列表页-1) * [博客详情页](http://growth-in-action.phodal.com/#博客详情页) * [添加导航](http://growth-in-action.phodal.com/#添加导航-1) * [配置管理](http://growth-in-action.phodal.com/#配置管理) * [local settings](http://growth-in-action.phodal.com/#local-settings) License --- [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) © 2015~2016 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory. [![待我代码编成,娶你为妻可好](http://brand.phodal.com/slogan/slogan.svg)](http://www.xuntayizhan.com/person/ji-ke-ai-qing-zhi-er-shi-dai-wo-dai-ma-bian-cheng-qu-ni-wei-qi-ke-hao-wan/)

E-book Management
2.5K Github Stars
fe
Open Source

fe

我的职业是前端工程师 === 2017 年 1 月份,看完村上春树的新书《我的职业是一个小说家》,我便萌发了写一个《我的职业是前端工程师》系列文章的想法——以个人视角来看前端领域的各种技术。整个系列的文章大概有 15 篇左右,从我是如何成为一个前端工程师,到各种前端框架的知识。 在线阅读地址:[http://ued.party/](http://ued.party/) 关注我的微信公众号(扫描下面的二维码或搜索 Phodal),回复书籍,可下载电子书版本 ![QRCode](http://articles.phodal.com/qrcode.jpg) 目录 --- * [关于作者](#关于作者) * [序](#序) * [为什么不应该写一本前端书籍?](#为什么不应该写一本前端书籍) * [本书在讲些什么](#本书在讲些什么) * [关于《我的职业是前端工程师》](#关于我的职业是前端工程师) * [我要成为一个前端设计师](#我要成为一个前端设计师) * [漂亮的前台](#漂亮的前台) * [我要成为一个前端设计师](#我要成为一个前端设计师-1) * [入门不是应该很简单吗?](#入门不是应该很简单吗) * [前端之路](#前端之路) * [我的前端入门](#我的前端入门) * [我的第一个网站](#我的第一个网站) * [Copy/Paste from Cookbook](#copypaste-from-cookbook) * [开发工具](#开发工具) * [jQuery 是最好用的](#jquery-是最好用的) * [如何选择合适的前端语言](#如何选择合适的前端语言) * [JavaScript 语言的变化](#javascript-语言的变化) * [JavaScript](#javascript) * [ES6+](#es6) * [TypeScript](#typescript) * [小结](#小结) * [如何选择合适的前端框架](#如何选择合适的前端框架) * [前端的选择恐惧症](#前端的选择恐惧症) * [技术选型:不仅仅受技术影响](#技术选型不仅仅受技术影响) * [上线时间影响框架](#上线时间影响框架) * [锤子定律:你需要更大的视野](#锤子定律你需要更大的视野) * [前端框架一览](#前端框架一览) * [jQuery, 使用生态解决问题](#jquery-使用生态解决问题) * [Backbone.js,脊椎连接框架](#backbone.js脊椎连接框架) * [Angular,一站式提高生产力](#angular一站式提高生产力) * [React,组件化提高复用](#react组件化提高复用) * [Vue.js,简单也是提高效率](#vue.js简单也是提高效率) * [小结](#小结-1) * [总结](#总结) * [必会的六个调试技能](#必会的六个调试技能) * [我的调试入门](#我的调试入门) * [基本调试技巧:实时调试](#基本调试技巧实时调试) * [实时调试样式](#实时调试样式) * [实时调试代码](#实时调试代码) * [移动设备调试](#移动设备调试) * [模拟真机:设备模拟器](#模拟真机设备模拟器) * [真机调试:Device Inspect](#真机调试device-inspect) * [网络调试](#网络调试) * [网络调试](#网络调试-1) * [使用插件](#使用插件) * [小结](#小结-2) * [如何以正确的姿势练习](#如何以正确的姿势练习) * [前端项目的练习过程](#前端项目的练习过程) * [Output is Input](#output-is-input) * [练习框架、技术的时机](#练习框架技术的时机) * [练习的过程](#练习的过程) * [练习框架、技术的技巧](#练习框架技术的技巧) * [使用模板](#使用模板) * [做点什么应用](#做点什么应用) * [编写一个博客应用](#编写一个博客应用) * [输入和总结](#输入和总结) * [其它](#其它) * [关于练手项目](#关于练手项目) * [前后端分离,你应该知道的八件事](#前后端分离你应该知道的八件事) * [前后端分离](#前后端分离) * [什么是前后端分离?](#什么是前后端分离) * [真的需要前后端分离吗?](#真的需要前后端分离吗) * [前后端分离将遇到的那些挑战](#前后端分离将遇到的那些挑战) * [前后端分离的核心:后台提供数据,前端负责显示](#前后端分离的核心后台提供数据前端负责显示) * [输出逻辑:数据显示](#输出逻辑数据显示) * [不可避免的前端逻辑:表单](#不可避免的前端逻辑表单) * [SEO 优化技巧](#seo-优化技巧) * [搜索引擎优化都是前端的活](#搜索引擎优化都是前端的活) * [如何设计一个高质量的 URL](#如何设计一个高质量的-url) * [受 RESTful API 影响 的 URL 设计](#受-restful-api-影响-的-url-设计) * [手动自定义 URL](#手动自定义-url) * [详情页 :简单的 URL 生成规则](#详情页-简单的-url-生成规则) * [自动化 URL:分类与多级目录](#自动化-url分类与多级目录) * [搜索结果页:将参数融入 URL](#搜索结果页将参数融入-url) * [自动生成高质量的站点标题](#自动生成高质量的站点标题) * [什么是站点标题?](#什么是站点标题) * [什么才算一个高质量的站点标题?](#什么才算一个高质量的站点标题) * [单页面应用的核心知识](#单页面应用的核心知识) * [单页面应用的演进](#单页面应用的演进) * [路由:页面跳转与模块关系](#路由页面跳转与模块关系) * [数据:获取与鉴权](#数据获取与鉴权) * [数据展示:模板引擎](#数据展示模板引擎) * [交互:事件与状态管理](#交互事件与状态管理) * [组件交互:状态管理](#组件交互状态管理) * [用户交互:事件](#用户交互事件) * [客户端存储与模型的艺术](#客户端存储与模型的艺术) * [模型与存储](#模型与存储) * [存储](#存储) * [模型的变化](#模型的变化) * [如何优化前端应用性能](#如何优化前端应用性能) * [博客优化经验:速度优化](#博客优化经验速度优化) * [TTFB 优化](#ttfb-优化) * [服务器优化](#服务器优化) * [项目优化经验:缓存优化](#项目优化经验缓存优化) * [移动优化经验:用户体验优化](#移动优化经验用户体验优化) * [缓存 API 结果](#缓存-api-结果) * [生命周期优化](#生命周期优化) * [优化中的反最佳实践](#优化中的反最佳实践) * [移动应用选型指南](#移动应用选型指南) * [Web 应用与混合应用](#web-应用与混合应用) * [性能](#性能) * [选型指南](#选型指南) * [React Native](#react-native) * [选型指南](#选型指南-1) * [NativeScript](#nativescript) * [Weex及其他](#weex及其他) * [如何处理好前后端分离的 API 问题](#如何处理好前后端分离的-api-问题) * [前后端分离 API 的演进史](#前后端分离-api-的演进史) * [瀑布式开发的 API 设计](#瀑布式开发的-api-设计) * [API 的协作设计](#api-的协作设计) * [使用文档规范 API](#使用文档规范-api) * [契约测试:基于持续集成与自动化测试](#契约测试基于持续集成与自动化测试) * [前端测试与 API 适配器](#前端测试与-api-适配器) * [小结](#小结-3) * [如何从头开发一个前端应用](#如何从头开发一个前端应用) * [前端应用的生命周期](#前端应用的生命周期) * [项目准备](#项目准备) * [技术选型](#技术选型) * [构建系统](#构建系统) * [前后端分离设计](#前后端分离设计) * [实现功能](#实现功能) * [分析设计图](#分析设计图) * [实现功能](#实现功能-1) * [编写测试](#编写测试) * [上线](#上线) 前端技能 --- 来源:[https://github.com/phodal/awesome-growth](https://github.com/phodal/awesome-growth) - 基础 * HTML / CSS * JavaScript * DOM - 中级篇 * 数据格式(如JSON、XML) * RESTful API交互(如jQuery Ajax,Fetch API,ReactiveX) * 正则表达式 * HTML语义化 * 命令行 * Node.js * DIV / CSS * SCSS / SASS * 矢量图形 / 矢量图形动画(如SVG) * 单页面应用 - 高级篇 * ES6 / TypeScript * CSS3 * 面向对象编程 * 函数式编程 * MVC / MVVM / MV* * 安全性(如跨域) * 授权(如HTTP Basic、JWT等等) - 工程化 * 代码质量(如JSLint / ESLint / TSLint / CSLint) * 代码分析(如Code Climate) * 测试覆盖率 * 构建系统(gulp、grunt、webpack等等) * 自动构建(脚本) - 兼容性 * 跨浏览器测试 (Chrome,IE,Firefox,Safari等等) * 跨平台测试(Windows、GNU/Linux,Mac OS等等) * 跨设备测试(Desktop,Android,iOS,Windows Phone) * 跨版本测试(同一个浏览器的不同版本) - 前端特定 * CSS / CSS3 动画 * JavaScript 动画 * Web字体嵌入 * Icon 字体 * 图形和图表 * CSS Sprite(如glue) * DOM操作(如jQuery、React等等) * 模板引擎(如JSX、Handlebars、JSP、Mustache等等) - 软件工程 * 版本管理(如git、svn) * 包管理(如npm、bower) * 依赖管理 * 模块化(如CommonJS、WebPack) - 调试 * 浏览器调试 * Debug工具 * Wireshark / Charles抓包 * 远程设备调试(如Chrome Inspect Devices) - 测试 * 单元测试 * 服务测试 * UI测试 * 集成测试 - 性能与优化 * PageSpeed / Yslow 优化 * 加载优化(如gzip压缩、缓存等等) * 性能测试(特别是移动Web) * 可用性 * 压缩(如Minify、Uglify、CleanCSS等等) - 设计 * 切页面 * 线框图(Wireframe) * 响应式设计 * 网格布局(Grid Layout) * Flexbox布局 - SEO * Sitemap(站点地图) * 内部链接建设 * MicroData / MicroFormat * 页面静态内容生成 * H1、H2、H3和strong使用 * Title、Description优化 * 页面静态内容生成 LICENSE --- [![Phodal's Article](http://brand.phodal.com/shields/article-small.svg)](https://www.phodal.com/) [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) © 2017 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory. [待我代码编成,娶你为妻可好](http://www.xuntayizhan.com/blog/ji-ke-ai-qing-zhi-er-shi-dai-wo-dai-ma-bian-cheng-qu-ni-wei-qi-ke-hao-wan/)

E-book Management
2K Github Stars
serverless
Open Source

serverless

Serverless 架构应用开发指南 === > Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。在这种架构中,我们并不看重运行一个函数需要多少 CPU 或 RAM 或任何其他资源,而是更看重运行函数所需的时间,我们也只为这些函数的运行时间付费。[^serverless] [^serverless]: http://www.infoq.com/cn/news/2017/04/2017-Serverless ``注意事项`` 在本系列的文章中,主要采用了 Serverless Framework 来简化开发和部署流程。 > Serverless Framework是无服务器应用框架和生态系统,旨在简化开发和部署AWS Lambda应用程序的工作。Serverless Framework 作为 Node.js NPM 模块提供,填补了AWS Lambda 存在的许多缺口。它提供了多个样本模板,可以迅速启动 AWS Lambda 开发。 Architecture --- ![Serverless Application Architecture](images/serverless-spa-architecture.png) 目录 --- * [Serverless 架构应用开发指南](http://serverless.ink/#serverless-架构应用开发指南) * [Serverless 架构](http://serverless.ink/#serverless-架构) * [什么是 Serverless 架构??](http://serverless.ink/#什么是-serverless-架构) * [虚拟化与隔离](http://serverless.ink/#虚拟化与隔离) * [为什么是花了 1000G ?](http://serverless.ink/#为什么是花了-1000g) * [Serverless 是什么?](http://serverless.ink/#serverless-是什么) * [事件驱动编程](http://serverless.ink/#事件驱动编程) * [Serverless 的优势](http://serverless.ink/#serverless-的优势) * [降低启动成本](http://serverless.ink/#降低启动成本) * [实现快速上线](http://serverless.ink/#实现快速上线) * [系统安全性更高](http://serverless.ink/#系统安全性更高) * [适应微服务架构](http://serverless.ink/#适应微服务架构) * [自动扩展能力](http://serverless.ink/#自动扩展能力) * [Serverless 的问题](http://serverless.ink/#serverless-的问题) * [不适合长时间运行应用](http://serverless.ink/#不适合长时间运行应用) * [完全依赖于第三方服务](http://serverless.ink/#完全依赖于第三方服务) * [冷启动时间](http://serverless.ink/#冷启动时间) * [缺乏调试和开发工具](http://serverless.ink/#缺乏调试和开发工具) * [构建复杂](http://serverless.ink/#构建复杂) * [语言版本落后](http://serverless.ink/#语言版本落后) * [Serverless 的适用场景](http://serverless.ink/#serverless-的适用场景) * [发送通知](http://serverless.ink/#发送通知) * [WebHook](http://serverless.ink/#webhook) * [轻量级 API](http://serverless.ink/#轻量级-api) * [物联网](http://serverless.ink/#物联网) * [数据统计分析](http://serverless.ink/#数据统计分析) * [Trigger 及定时任务](http://serverless.ink/#trigger-及定时任务) * [精益创业](http://serverless.ink/#精益创业) * [Chat 机器人](http://serverless.ink/#chat-机器人) * [其它](http://serverless.ink/#其它) * [迁移方案](http://serverless.ink/#迁移方案) * [Serverless Framework](http://serverless.ink/#serverless-framework) * [Apex](http://serverless.ink/#apex) * [Apache OpenWhisk](http://serverless.ink/#apache-openwhisk) * [Serverless 的 hello, world](http://serverless.ink/#serverless-的-hello-world) * [Serverless 框架 hello, world](http://serverless.ink/#serverless-框架-hello-world) * [一、安装 serverless 框架](http://serverless.ink/#一安装-serverless-框架) * [二、设置 aws 凭证。](http://serverless.ink/#二设置-aws-凭证) * [三、创建 hello-world 服务](http://serverless.ink/#三创建-hello-world-服务) * [四、部署及测试:](http://serverless.ink/#四部署及测试) * [AWS Lambda 动态编程返回 HTML](http://serverless.ink/#aws-lambda-动态编程返回-html) * [将网站部署到 S3 上](http://serverless.ink/#将网站部署到-s3-上) * [配置 serverless-finch](http://serverless.ink/#配置-serverless-finch) * [静态内容](http://serverless.ink/#静态内容) * [为基于 S3 的网站支持 CRUD](http://serverless.ink/#为基于-s3-的网站支持-crud) * [概念:API Gateway 与 S3](http://serverless.ink/#概念api-gateway-与-s3) * [基于 S3 的 Serverless CRUD](http://serverless.ink/#基于-s3-的-serverless-crud) * [上传原理](http://serverless.ink/#上传原理) * [Serverless S3 CRUD 示例](http://serverless.ink/#serverless-s3-crud-示例) * [结合 DynamoDB 数据库创建 RESTful API](http://serverless.ink/#结合-dynamodb-数据库创建-restful-api) * [Serverless DynamoDB 示例配置](http://serverless.ink/#serverless-dynamodb-示例配置) * [Serverless DynamoDB 示例代码](http://serverless.ink/#serverless-dynamodb-示例代码) * [Serverless DynamoDB 部署](http://serverless.ink/#serverless-dynamodb-部署) * [Serverless DynamoDB 测试](http://serverless.ink/#serverless-dynamodb-测试) * [其它操作](http://serverless.ink/#其它操作) * [Express 实现 Serverless 的 React 服务端渲染](http://serverless.ink/#express-实现-serverless-的-react-服务端渲染) * [Serverless + Express](http://serverless.ink/#serverless-express) * [Express + React 进行服务端渲染](http://serverless.ink/#express-react-进行服务端渲染) * [Serverless 的微信公共平台后台](http://serverless.ink/#serverless-的微信公共平台后台) * [创建 Serverless 服务](http://serverless.ink/#创建-serverless-服务) * [引入 node-wechat](http://serverless.ink/#引入-node-wechat) * [配置 APP_ID 和 TOKEN 等](http://serverless.ink/#配置-app_id-和-token-等) * [配置 Route 53 与 API Gateway](http://serverless.ink/#配置-route-53-与-api-gateway) * [添加微信公众平号服务](http://serverless.ink/#添加微信公众平号服务) * [部署](http://serverless.ink/#部署) * [基于 Kinesis Streams 的数据流分析](http://serverless.ink/#基于-kinesis-streams-的数据流分析) * [Amazon Kinesis Streams](http://serverless.ink/#amazon-kinesis-streams) * [Serverless + Kinesis Streams](http://serverless.ink/#serverless-kinesis-streams) * [Serverless 数据分析,Kinesis Firehose 持久化数据到 S3](http://serverless.ink/#serverless-数据分析kinesis-firehose-持久化数据到-s3) * [Serverless Kinesis Firehose 代码](http://serverless.ink/#serverless-kinesis-firehose-代码) * [安装及测试](http://serverless.ink/#安装及测试) * [创建邮件发送 API](http://serverless.ink/#创建邮件发送-api) * [Serverless Email 发送](http://serverless.ink/#serverless-email-发送) * [Serverless Email 发送测试](http://serverless.ink/#serverless-email-发送测试) * [创建自己的 Serverless 短链服务](https://www.phodal.com/blog/serverless-architecture-develoment-create-self-short-url-services/) * [代码逻辑](http://serverless.ink/#代码逻辑) * [创建首页](http://serverless.ink/#创建首页) * [生成短链](http://serverless.ink/#生成短链) * [解析出提交表单中的链接](http://serverless.ink/#解析出提交表单中的链接) * [根据 URL 生成对应的短链](http://serverless.ink/#根据-url-生成对应的短链) * [存储到 Dynamo 数据库中。](http://serverless.ink/#存储到-dynamo-数据库中) * [返回短链给用户](http://serverless.ink/#返回短链给用户) * [重定向短链](http://serverless.ink/#重定向短链) * [部署及测试短链服务](http://serverless.ink/#部署及测试短链服务) * [结合 Auth0 的登录、授权系统](http://serverless.ink/#结合-auth0-的登录授权系统) * [Serverless Auth0 前端代码](http://serverless.ink/#serverless-auth0-前端代码) * [Serverless Auth0 后台代码](http://serverless.ink/#serverless-auth0-后台代码) * [serverless.yml 配置](http://serverless.ink/#serverless.yml-配置) * [配置及部署](http://serverless.ink/#配置及部署) * [清理](http://serverless.ink/#清理) * [结论](http://serverless.ink/#结论) * [在本地部署与调试](http://serverless.ink/#在本地部署与调试) * [serverless-offline](http://serverless.ink/#serverless-offline) * [本地搭建 serverless-offline 与 DynamoDB 环境](http://serverless.ink/#本地搭建-serverless-offline-与-dynamodb-环境) * [本地测试 serverless-offline 与 DynamoDB](http://serverless.ink/#本地测试-serverless-offline-与-dynamodb) * [如何编写 Serverless 应用的测试](http://serverless.ink/#如何编写-serverless-应用的测试) * [Serverless 应用的测试](http://serverless.ink/#serverless-应用的测试) * [步骤](http://serverless.ink/#步骤) * [创建测试](http://serverless.ink/#创建测试) * [运行测试](http://serverless.ink/#运行测试) * [更准确的测试](http://serverless.ink/#更准确的测试) * [结论](http://serverless.ink/#结论-1) * [多个语言运行环境](http://serverless.ink/#多个语言运行环境) * [Serverless 多个语言运行环境](http://serverless.ink/#serverless-多个语言运行环境) * [部署及测试](http://serverless.ink/#部署及测试) * [Serverless 开发物联网应用](http://serverless.ink/#serverless-开发物联网应用) * [Serverless 框架安装服务](http://serverless.ink/#serverless-框架安装服务) * [部署 AWS IoT Serverless 服务](http://serverless.ink/#部署-aws-iot-serverless-服务) * [查看日志](http://serverless.ink/#查看日志) * [使用 warmup 插件保活,避免应用冷启动](http://serverless.ink/#使用-warmup-插件保活避免应用冷启动) * [使用 serverless-plugin-warmup 保持唤醒](http://serverless.ink/#使用-serverless-plugin-warmup-保持唤醒) * [部署及测试](http://serverless.ink/#部署及测试-1) * [结论](http://serverless.ink/#结论-2) * [定时执行任务](http://serverless.ink/#定时执行任务) * [Serverless 定时任务](http://serverless.ink/#serverless-定时任务) * [rate 表达式](http://serverless.ink/#rate-表达式) * [cron 表达式](http://serverless.ink/#cron-表达式) * [部署](http://serverless.ink/#部署-1) * [使用 Simulate 插件在本地运行 Lambda](http://serverless.ink/#使用-simulate-插件在本地运行-lambda) * [serverless-plugin-simulate 插件](http://serverless.ink/#serverless-plugin-simulate-插件) * [安装 serverless-plugin-simulate 及环境](http://serverless.ink/#安装-serverless-plugin-simulate-及环境) * [运行及测试](http://serverless.ink/#运行及测试) * [使用 GraphQL 实现更好的 API](http://serverless.ink/#使用-graphql-实现更好的-api) * [GraphQL hello, world](http://serverless.ink/#graphql-hello-world) * [更复杂的示例](http://serverless.ink/#更复杂的示例) * [GraphQL 修改 DymanoDB 的值](http://serverless.ink/#graphql-修改-dymanodb-的值) * [Serverless 应用示例:前端错误日志及事件收集系统](http://serverless.ink/#serverless-应用示例前端错误日志及事件收集系统) * [Molog 使用](http://serverless.ink/#molog-使用) * [Serverless 错误收集系统架构设计](http://serverless.ink/#serverless-错误收集系统架构设计) * [架构设计](http://serverless.ink/#架构设计) * [Molog 系统实现](http://serverless.ink/#molog-系统实现) * [1\. 存储日志和事件](http://serverless.ink/#存储日志和事件) * [2\. 读取日志](http://serverless.ink/#读取日志) * [问题](http://serverless.ink/#问题) * [Serverless 应用示例:二维码生成](http://serverless.ink/#serverless-应用示例二维码生成) * [总览](http://serverless.ink/#总览) * [生成应用](http://serverless.ink/#生成应用) * [配置资源](http://serverless.ink/#配置资源) * [生成二维码](http://serverless.ink/#生成二维码) * [上传 AWS S3](http://serverless.ink/#上传-aws-s3) * [优化二维码](http://serverless.ink/#优化二维码) * [部署及测试](http://serverless.ink/#部署及测试-2) License --- [![Phodal's Article](http://brand.phodal.com/shields/article-small.svg)](https://www.phodal.com/) [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) © 2017 [Phodal Huang](https://www.phodal.com). The **code** is distributed under the MIT License. See `LICENSE` in this directory. © 2017 [Phodal Huang](https://www.phodal.com). The **content** is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory. [待我代码编成,娶你为妻可好](http://www.xuntayizhan.com/blog/ji-ke-ai-qing-zhi-er-shi-dai-wo-dai-ma-bian-cheng-qu-ni-wei-qi-ke-hao-wan/)

E-book Management FaaS & Serverless
1.7K Github Stars
aigc
Open Source

aigc

# 构筑大语言模型应用:应用开发与架构设计 > aka. Unlocking the Potential of Large Language Models: Real-World Use Cases 2023 年的上半年里,我(@phodal)和 Thoughtworks 的同事们(如:@[tianweiliu](https://github.com/tianweiliu)、@[teobler](https://github.com/teobler)、@[mutoe](https://github.com/mutoe) 等)、 开源社区的同伴们(如: 卷王@[CGQAQ](https://github.com/CGQAQ)、@[genffy](https://github.com/genffy)、 @[liruifengv](https://github.com/liruifengv) 等) 一起,创建了一系列的流行的或者不流行的开源项目。它们涉及了: - LLM 能力的充分运用 - Prompt 编写:Prompt 学习与编写模式 - Prompt 管理:Prompt 即代码 - LLM 下的软件开发工序及应用架构设计 - 新的交互设计:Chat 模式 - 大模型友好的工序:基于 AI 2.0 (ChatGPT + Copilot)如何去设计软件开发流程 - LLM 应用架构的设计与落地:Unit Mesh - 面向特定场景的 LLM 应用 - 基于开源模型构建自己的模型:特定场景的模型微调 + LLMOps - 上下文工程(prompt 工程):LLM 应用的核心 围绕于上述的一系列内容,我们也在思考软件开发能给我们带来了什么。所以,我重新整理了过去半年的一些思考、文章,重新编写了这本开源电子书,希望能够帮助到大家。 关注我的微信公众号(搜索 phodal-weixin),获得更多及时的更新: ![微信公众号](src/images/qrcode.jpg) 我们发起的相关开源项目如下(包括但是不限于): | 名称 | 描述 | 类型 | Stars | |----------------------------------------------------------------------------|------------------------------------------------------------------------------------------|-------------|------------------------------------------------------------------------------------------------| | [理解 Prompt](https://github.com/prompt-engineering/understand-prompt) | 基于编程、绘画、写作的 AI 探索与总结。 | 文档 | ![GitHub Repo stars](https://img.shields.io/github/stars/prompt-engineering/understand-prompt) | | [Prompt 编写模式](https://github.com/prompt-engineering/prompt-patterns) | 如何将思维框架赋予机器,以设计模式的形式来思考 prompt。 | 文档 | ![GitHub Repo stars](https://img.shields.io/github/stars/prompt-engineering/prompt-patterns) | | [ClickPrompt](https://github.com/prompt-engineering/click-prompt) | 用于一键轻松查看、分享和执行您的 Prompt。 | 应用 | ![GitHub Repo stars](https://img.shields.io/github/stars/prompt-engineering/click-prompt) | | [ChatVisualNovel](https://github.com/prompt-engineering/chat-visual-novel) | 基于 ChatGPT 的定制化视觉小说引擎 | 应用 | ![GitHub Repo stars](https://img.shields.io/github/stars/prompt-engineering/chat-visual-novel) | | [ChatFlow](https://github.com/prompt-engineering/chat-flow) | 打造个性化 ChatGPT 流程,构建自动化之路。 | 框架 | ![GitHub Repo stars](https://img.shields.io/github/stars/prompt-engineering/chat-flow) | | [Unit Mesh](https://github.com/unit-mesh/unit-mesh) | 基于 AI 为核心的软件 2.0 思想的软件架构。 | 架构 | ![GitHub Repo stars](https://img.shields.io/github/stars/unit-mesh/unit-mesh) | | [Unit Minions](https://github.com/unit-mesh/unit-minions) | AI 研发提效研究:自己动手训练 LoRA | 微调教程、指南、数据集 | ![GitHub Repo stars](https://img.shields.io/github/stars/unit-mesh/unit-minions) | | [Unit Runtime](https://github.com/unit-mesh/unit-runtime) | 一个 ChatGPT 等 AI 代码的运行环境,可一键启动并实时交互,帮助您快速构建和测试 AI 代码。 | 基础设施 | ![GitHub Repo stars](https://img.shields.io/github/stars/unit-mesh/unit-runtime) | | [DevTi](https://github.com/unit-mesh/devti) | 基于 LLM 的微调来提供全面智能化解决方案,助力开发人员高效完成开发任务,以实现自动化用户任务拆解、用户故事生成、自动化代码生成、自动化测试生成等等。 | 微调代码 | ![GitHub Repo stars](https://img.shields.io/github/stars/unit-mesh/devti) | | [AutoDev](https://github.com/unit-mesh/auto-dev) | 一款 Intellij IDEA 的 LLM/AI 辅助编程插件。AutoDev 能够与您的需求管理系统(例如 Jira、Trello、Github Issue 等)直接对接。 | IDEA 插件 | ![GitHub Repo stars](https://img.shields.io/github/stars/unit-mesh/auto-dev) | | [ArchGuard Co-mate](https://github.com/archguard/co-mate) | 基于人工智能技术的架构副驾驶、设计和治理工具 | 架构协同应用 | ![GitHub Repo stars](https://img.shields.io/github/stars/archguard/co-mate) | 我们在 QCon 上的演讲:[演讲:探索软件开发新工序:LLM 赋能研发效能提升](https://qcon.infoq.cn/2023/guangzhou/presentation/5319) > LLM(如 ChatGPT + GitHub > Copilot)作为一种创新的工具组合,为我们带来了全新的机遇。它能够帮助业务人员和开发者在需求、架构、编码、测试等环节提高效率和质量,实现从设计到验证的端到端流程。在本次分享中,我将向大家介绍 > LLM 在研发效能方面的应用场景和实践案例,展示它是如何在各个环节中发挥作用的。同时,我们还将分享如何构建私有化的 LLM > 工程化方式,使其更好地适应组织的需求。欢迎对 LLM + 研发效能感兴趣的朋友们参加本次分享,与我们一起探讨研发效能的未来。 我们在 Bilibili 上的大语言模型微调相关的视频: - LLaMA 系列在线视频: 《[代码辅助生成](https://www.bilibili.com/video/BV1Rh411u74H/)》 、《[测试代码生成](https://www.bilibili.com/video/BV1jg4y1G7Xc/)》 、《[详细需求生成](https://www.bilibili.com/video/BV1Us4y1N7rd/)》 、《[文本转 SQL](https://www.bilibili.com/video/BV1uv4y1H7bg/)》 - ChatGLM 系列在线视频: 《[LoRA 大比拼:ChatGLM vs LLaMA,谁更会写需求文档?](https://www.bilibili.com/video/BV1fv4y1n7Y3/)》 欢迎大家一起来参与我们的开源项目,一起来探索 LLM + 软件开发的未来。

AI Agents E-book Management
1.6K Github Stars
dx-book
Open Source

dx-book

# 开发者体验:探索与重塑 ![Logo](resources/dx.svg) 开发者体验,与用户体验类似,只是对象是软件开发人员。因此,它的定义是 > 开发者体验是开发人员对于针对使用或期望使用的产品、系统或者服务的认知印象和回应。有所不同的是,用户关注的内容变为库,SDK,文档,框架,开源解决方案,通用工具,API 等的开发人员的体验。 2019,在经历了多个低代码前端项目的售前,以及一个低代码项目的技术实践强化,发现国内的 IT 企业缺乏对于『开发者体验』缺乏系统性的思考。便想着结合自己的项目经历、社区经验、国内外实际情况等,编写一个简要的开发者体验电子书。 在线阅读:[https://dx.phodal.com](https://dx.phodal.com) 文档目录:[docs](./docs) 模板:[https://github.com/pmarsceill/just-the-docs](https://github.com/pmarsceill/just-the-docs) 欢迎参与讨论与 PR: ![Wechat QRCode](image/qrcode.jpg) (PS:如果群满了,请添加我的微信 `phodal02` ) ## Todo - [x] define dex - [x] maturity model - [x] define - [ ] refs model - [x] operation model - [ ] practise - [x] documentation - [ ] error handling (ongoing) - [ ] interactive design - [ ] usability - [ ] developer relation - [ ] anti patterns - [x] developer as services ## License [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) © 2020~2021 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory.

E-book Management Documentation
290 Github Stars
aise
Open Source

aise

# 《AI 辅助软件工程:实践与案例解析》 ![Cover](src/images/cover.jpeg) 2024 年 6 月 1 日,我计划利用在 Thoughtworks 的三个月年资假做一些 AI 辅助研究与超前设计。同时结合之前的 AI 辅助软件工程实践经验以及在 Thoughtworks 的开源项目与落地经验,编写一本关于《AI辅助软件工程》的开源电子书。 - 作为一个新晋奶爸,我需要在带娃的业余时间里完成这本书,所以有些调研会比较仓促和写作进度可能会比较慢。但是,我会尽量保证每周至少更新一篇文章。 - 作为一个工程师,我更喜欢能真正落地的方案,而不是纸上谈兵。所以,我花了大量的时间在收集和分析落地案例,以及在实际项目中的实践经验,以避免浪费读者的时间。 这本开源电子书( https://aise.phodal.com/ )包含了什么: - 国内外最新的 AI 辅助软件工程实践。我们阅读了大量的国内外生成式 AI 技术博客,收集了从 GitHub、GitLab、Google、JetBrains、Atlassian、New Relic/Dynatrace 等公司的 AI 辅助软件工程实践。 - 从 AI 模型到 IDE 插件的实现细节。作为 IDE 插件 AutoDev 开源项目以及 Shire 编码智能体语言的作者,我会结合实际项目经验,分享如何从 AI 模型到 IDE 插件的实现细节。 - 从编码 RAG 到编码智能体的实践案例与经验。我们收集了大量的编码智能体的实践案例与经验,以及在编码领域如何使用 RAG 模型的实践案例。 这本书没有什么? - 相关 Paper。作为一个工程师,我更喜欢能真正落地的方案,而不是纸上谈兵。尽管已经有大量的 AI 辅助软件工程的 Paper,从业内的实践经验来看,Paper 与实际落地的差距还是很大的。 如果你对此感兴趣,欢迎加入这个开源项目。 阅读需知: 在大多数组织中,开发者用于编码的时间不到总工作时间的 40%,因此编码提效对整个软件开发生命周期(SDLC)的效能影响很有限,应尝试将 AI 应用于软件 全生命周期和整个团队,而不仅仅是个别成员、个别环节的产出。 更多关于我的信息,请访问我的个人网站:[Phodal](https://www.phodal.com)。或者扫描下方二维码: ![](https://articles.phodal.com/qrcode.jpg) ## 简介 本书是一本关于如何使用 AI 辅助软件工程的开源电子书。我们将介绍: - 如何使用生成式 AI 来在软件工程的不同阶段进行辅助。 - AI IDE 编程工具的 Prompt 技巧。 - 设计 AI 辅助流程来提高软件工程师的工作效率。 - 定制 AI IDE 来融入不同编程任务。 - 设计和构建 AI 编码智能体。 - 如何使用 Shire 与 RAG 来进行 AI 辅助研发。 开始阅读之前,你需要自行了解一些基本概念: - 什么是生成式 AI? - AI 智能体是什么? - 什么是 IDE 插件?(我想你是不需要的) 本书不适合以下人群: - 想要了解 AI 基础知识的人。 - 快速入门使用 AI 辅助软件工程的人。 - 想要了解 AI IDE 插件开发的人。 如果你是以上人群,建议你先了解一些基础知识,然后再来阅读本书。

AI & Machine Learning E-book Management
196 Github Stars