Blog | Phodal - A Growth Engineerhttp://www.phodal.com/blog/2023-02-17T03:14:25.560995+00:00BlogKotlin Dataframe 与 Apache Arrow 的一次尝试(基于 DEMO 的 ChatGPT 生成)2023-02-17T03:13:36+00:002023-02-17T03:14:25.560995+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/dataframe-arrow-with-chatgpt-demo/(PS:尝试结合 ChatGPT 生成)
Dataframe Arrow 是基于 Apache Arrow 的 Kotlin Dataframe 实现,目标是提供高性能的数据处理框架,同时保持与 Apache Arrow 的兼容性。
本文将探讨 Dataframe Arrow 的应用以及 Kotlin Dataframe 与 Apache Arrow 的关系。
## 为什么 Dataframe Arrow 是可接受的
Kotlin Dataframe Arrow 的优点:
1. 高性能:由于使用了 Apache Arrow 的数据结构,可以实现高效的数据处理和传输。
2. 数据格式标准化:Apache Arrow 提供了一种标准的跨语言内存布局,Kotlin Dataframe Arrow 的实现与 Apache Arrow 的兼容性非常高,从而可以实现数据格式的标准化,使得数据的处理更加方便。
3. 易于使用:Kotlin Dataframe Arrow 为数据处理和传输提供了一种简单、易于使用的框架,可用于数据挖掘、机器学习、数据分析等领域。在 Kotlin 中使用 Dataframe 也非常方便,只需要定义 Dataframe Schema 和读取 Dataframe 即可。
除此,对于 ArchGuard 的 CLI 来说,体积也是很重要的,如下是积的差异:
- Apache Arrow with Chapi: 14M
- Jetbrains Dataframe with Chapi: 35M
- Jetbrains Dataframe Arrow with Chapi: 19M
Jetbrains Dataframe Arrow 依赖于 Apache Arrow,4M 的差异还是可以接受的。
## Apache Arrow Schema 与 FlatBuffer Schema
Apache Arrow Schema 与 FlatBuffer Schema 是两种不同的 Schema 定义方式,其区别在于:
- Apache Arrow Schema 是使用 JSON 格式。
- FlatBuffer Schema 是使用自定义的 FlatBuffer Schema 格式。
我们来看一个示例。假设我们有一个待序列化的 Kotlin 类:
```kotlin
@Serializable
data class CodePosition(
var StartLine: Int = 0,
var StartLinePosition: Int = 0,
var StopLine: Int = 0,
var StopLinePosition: Int = 0
)
```
那么,这个类的 FlatBuffer Schema 定义为:
```flatbuffer
table Position {
StartLine: int;
StartLinePosition: int;
StopLine: int;
StopLinePosition: int;
}
```
而这个类的 Apache Arrow Schema 则可以用以下的 JSON 表示:
```json
{
"name": "Position",
"type": {
"name": "Position",
"type": "struct",
"fields": [
{
"name": "StartLine",
"type": "int"
},
{
"name": "StartLinePosition",
"type": "int"
},
{
"name": "StopLine",
"type": "int"
},
{
"name": "StopLinePosition",
"type": "int"
}
]
}
}
```
对于 Apache Arrow Schema 的 Kotlin 实现,可以使用以下代码:
```kotlin
fun main() {
val rootAllocator = RootAllocator(Long.MAX_VALUE)
val byteBuddyPool = ByteBuddyPool()
val schema = Schema(List<field>().k().apply {
add(Field.nullable("StartLine", ArrowType.Int(32, true)))
add(Field.nullable("StartLinePosition", ArrowType.Int(32, true)))
add(Field.nullable("StopLine", ArrowType.Int(32, true)))
add(Field.nullable("StopLinePosition", ArrowType.Int(32, true)))
}, null)
// Use the schema as needed
println(schema)
}
```
## 在 Kotlin 中使用 Dataframe
在 Kotlin 中使用 Dataframe,需要先定义 Dataframe 的 schema,然后可以使用 Dataframe API 进行数据的读取、过滤、聚合等操作。
下面介绍三种定义 Dataframe schema 的方式:
### 1. 定义 Dataframe Schema
在 Kotlin 中,可以使用类定义 Dataframe 的 schema。需要在类上添加 @DataSchema 注解,然后在类中定义属性作为 Dataframe 的列。属性需要使用 val 或 var 关键字定义,并指定属性的类型和名称。如下所示:
```kotlin
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
@DataSchema
interface Person {
val name: String
val age: Int
}
```
### 2. 使用注解
可以使用 @ImportDataSchema 注解来导入已有的 Dataframe schema,如下所示:
```kotlin
@file:ImportDataSchema(
"Repository",
"https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv",
)
import org.jetbrains.kotlinx.dataframe.annotations.ImportDataSchema
```
### 3. 使用 Gradle Task
定义好 schema 后,可以使用 Dataframe API 进行数据读取和处理,如下所示:
```kotlin
dataframes {
schema {
data = "https://raw.githubusercontent.com/Kotlin/dataframe/master/data/jetbrains_repositories.csv"
name = "org.example.Repository"
}
}
```
此外,Dataframe 还支持数据过滤、聚合等操作,具体使用可以参考 Kotlin Dataframe 的官方文档。
## Kotlin Dataframe 与 Apache Arrow 关系
Kotlin Dataframe是基于Apache Arrow的数据框架,它提供了对Arrow内存格式和Arrow表格格式的支持。因此,Kotlin Dataframe可以看作是Arrow在Kotlin中的实现。
Apache Arrow是一种跨语言的内存数据结构,可以有效地在不同的计算引擎和编程语言之间传递数据。Arrow的一个关键优势是它使用了内存映射文件(Memory Mapped Files)来管理数据,这使得跨语言和跨平台数据传输变得更加高效和可扩展。
Kotlin Dataframe 的依赖如下所示:
```gradle
implementation(libs.arrow.vector)
implementation(libs.arrow.format)
implementation(libs.arrow.memory)
implementation(libs.commonsCompress)
implementation(libs.kotlin.reflect)
implementation(libs.kotlin.datetimeJvm)
```
Kotlin Dataframe 依赖于 Arrow 的核心库和格式库,这些库提供了 Arrow 表格格式和内存格式的支持。除此之外,Kotlin Dataframe 还使用了 Kotlin 反射库和 Kotlin 日期时间库等其他第三方库。
## 注意事项
当使用 Kotlin Dataframe 时,还需要注意以下几点:
- 需要将数据类声明为 data class,以便 Dataframe 可以识别数据类的属性。
- 集合类型应该是 List、Map 或 ArrayList,而不是 Array,因为 Dataframe 对于集合的处理是基于 Java Collections Framework 的,而不是基于原生数组。
此外,还需要注意以下几点:
- 数据类的属性类型应该是 Kotlin 原生类型或者是具有无参构造函数的类,这是因为 Dataframe 可以直接将这些类型映射到 Arrow 数据类型,而无需进行其他转换。
- 如果数据类的属性类型是一个复合类型,比如一个列表或一个嵌套的数据类,需要使用 Arrow 的数据类型来定义这个属性的类型。
- 在定义 Dataframe schema 时,需要显式指定每个列的数据类型,否则 Dataframe 会根据数据自动推断数据类型,可能会导致类型不一致的错误。
- 在读取 CSV 或 Parquet 文件时,需要指定文件的 schema,否则 Dataframe 也会尝试根据数据自动推断数据类型,可能会导致类型不一致的错误。
## Kotlin Dataframe 问题
Kotlin Dataframe 是一个非常优秀的 Kotlin 数据框架,但是它也存在一些 问题:
- 不支持嵌套类型的数据,例如嵌套的列表或嵌套的数据类。详细见:[Nested type in dataframe is not supported yet](https://github.com/Kotlin/dataframe/issues/271)
- 对于数据的变更需要使用到 Arrow 库中的 Mutable 表示类型,不方便使用。
- 在数据量较小的情况下,使用 Kotlin Dataframe 的性能不如 Kotlin 内置的集合类型。
如果想要解决这些问题,可以考虑修改 Kotlin Dataframe Arrow 的源码
- arrowTypesMatching.kt
- ArrowWriterImpl.kt
此外,如果你不想修改 Kotlin Dataframe 源码,也可以考虑使用其他类库,例如:
- Ktorm:提供了强类型的 SQL 操作,与 Kotlin 集成良好。
- Exposed:提供了类似于 ActiveRecord 的模型操作方式,支持 DSL 查询语法。
- JOOQ:支持多种数据库类型,提供了完善的查询语法和类型安全。
以上是一些常用的 Kotlin 数据库操作类库,你可以根据自己的需求选择适合的工具</field>2022 节点:重学架构与居家厨艺2023-01-01T09:21:45+00:002023-01-01T09:22:24.478390+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/node-2022/2022 年充满了太多的变化。原定在 2022 年底写的本文,也因为新冠阳性推到了 2023。写这一篇文章的难点,并不是回顾这一年做了什么,而是基于这一年的感悟,做一些新的投资,选择新的举措。
回顾 2021 年的一些想法:
1. 知识管理体系工具 Quake。开了个头,依旧还需要在下一年重构。
2. 那本写了 1/3 的书。放弃了。
3. 新的语言练手。暂时没有,不过对于 Kotlin 也算使用得更好了。也是由此,我发现了问题,小众语言开发的开源项目,客户群太少。
4. 继续构建 Inherd 团队和开源项目。好像没有。
虽然都只做了一部分,不过由于目标更少了,所以做得更深了。
## 编码:工具与模型度量
在工具侧,在技能点上已经相当的成熟。在这一年里,主要是:
* 围绕于遗留系统现代化,创建了 GitHub 组织 [Modernizing](https://github.com/modernizing),并梳理了电子书:《[代码分析与自动化重构](https://github.com/modernizing/modernization)》。
* 围绕于架构治理,重构并开源了内部的架构治理工具 [ArchGuard](https://archguard.org/)
* 架构设计 DSL 体系:架构在线协作工具 [Feakin Web](https://github.com/feakin/feakin-web)、架构设计 DSL [Fklang](https://github.com/feakin/fklang) 以及对应的 [Intellij IDEA](https://github.com/feakin/intellij-feakin) 等。
又可以简化化两方面:
* **建模软件架构,寻找元模型。**
* **从自助服务设计,探索数据。**
与 2021 年相比,在模型设计、指标度量上做的事情更多了,发现工具的开发于我而言,并不具备挑战。所以,在 2023 年,有针对于性强化模型与指标能力,就成了一件理所当然的事情。诸如于,跨领域(DevOps、架构度量)复用的数字化平台及其基础设施。
## 写作:体系化输出
与上一年相比,2022 年的输出更体系化了,主要是在两部分:**架构治理**与**金融科技**。
* 架构治理相关文章:《[移动应用架构治理初探:从依赖分析与 Android 应用的生命周期说起](https://www.phodal.com/blog/android-architecture-governance-start-from-dependencies-analysis/)》、《[架构自治服务:构建数据驱动的架构洞察](https://www.phodal.com/blog/architecture-self-governance-service/)》、《[架构治理基石:基于规范 + 模式的工具化](https://www.phodal.com/blog/architecture-governance-rule-spec-pattern/)》、《[架构工作台:构建企业(应用)架构的数字孪生](https://www.phodal.com/blog/architecture-workbench/)》、《[如何实现一个架构工作台?来看看 ArchGuard 如何实现的](https://www.phodal.com/blog/build-architecture-workbench/)》、《[架构即代码:编码下一代企业(应用)架构体系](https://www.phodal.com/blog/architecture-as-code/)》、《[架构治理调研:规则、表达式还有语言](https://www.phodal.com/blog/architecture-govermance-rule-expression-and-language/)》、《[为“架构”再建个模:如何用代码描述软件架构?](https://www.phodal.com/blog/architecture-model/)》、《[架构孪生:数字化架构的虚拟模型是什么?](https://www.phodal.com/blog/architecture-twin/)》
* 金融科技相关技术文章:《[低延迟架构体系初探](https://www.phodal.com/blog/explore-low-latency-architecture/)》、《[金融 Python 即服务:业务自助的数据服务模式](https://www.phodal.com/blog/bank-python-as-a-service/)》、《[高性能可视化架构:一个交互式实时数据引擎的架构设计](https://www.phodal.com/blog/high-performance-frontend-component/)》、《[自动增量计算:构建高性能数据分析系统的任务编排](https://www.phodal.com/blog/incremental-computing-for-financial-python/)》 等。
在年底,我也尝试输出架构治理的相关模式,以方便于未来做更多的探索。
## 社区
和同事一起构建了 ArchGuard 社区,并在上面做了几次分享,详细见 B 站:[ArchGuard 的个人空间](https://space.bilibili.com/1934290457) 。只是呢,社区并不是很大,有可能是架构治理的难度所导致的,也可能是我并不擅长于此,需要进一度的研究。
在对外分享上。除了 ArchGuard 社区的分享,在自身擅长的开发体验等领域,在字节跳动内部分享了《开发者体验》等相关的内容。
## 设计
做了几个 Logo,诸如于 CodeDB、Chapi 的,也没有新的研究了。不过,我在思考,2023 年是否能强化一下 UI 设计的能力,以便于开始更 fancy 的前端 UI。
## 2023
回到上面,围绕于度量与数字化,需要构建一些数据的基础设施。诸如于正在计划中的:
* CodeDB/MetricDB。它并不是一个真正意义上的通用数据库,只是提供数据的封装。
* 自服务的度量组件。如 Meta Hierarchy、Self Service 组件等。
2023 年依旧还是充满挑战和变化,但愿我们能应对变化。技术的成长:如何从毕业生到技术专家?2022-08-15T12:40:33+00:002022-08-15T12:40:52.497383+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/growth-from-beginner-to-expert/过去的一个月里,在帮助其他部门进行毕业生培训。从名称上说是培训,但是实际上则是**训战**结合。不想一下子给太多,这篇文章会给的建议是:
1. 寻找持续成长的动机
2. 塑造整洁的编码习惯
3. 建立定位问题的方式
4. 学习既有的模式
5. 频繁性自我总结
只凭这五点来说,与《福格行为模型》所定义的三要素也是颇为相似的:
* 要素1 动机(Motivation):找到实现愿望的黄金行为
* 要素2 能力:让行为简单到随时顺便都能做
* 要素3 提示:善用锚点时刻让行为立刻发生
如果再简化来说,也可以采用和我一样的模式,通过基本简单的行为:每天写代码,每周做总结(通过文章)。
---
## 再次定义专家
再回到我们这篇文章的主题里,如何从毕业生到一个技术专家?专家是基于研究、经验或职业并在特定研究领域具有广泛知识或能力的人。这样的定义是如此的简洁,以至于一个工作经验丰富的人都可以称上得是专家。在这种定义之下,一个 996 的程序员的开发经验,可谓不比一个 965 的人差。
于是乎,我还更喜欢,我在在那篇《[专家 x 抽象 x 类比](https://www.phodal.com/blog/expert-abstract-analogy/)》里,我们也定义了专家应该做点什么?
> 所谓的专家嘛,就是在擅长的 “领域” 里,构建了具有范畴化(归类)的概念空间,并可以通过类比灵活地完善自己的概念库。
在这个定义之下,我们行业的技术专家便是指,在软件开发领域,具备丰富的软件开发相关的知识(即概念)或者是经验。拥有自己的软件开发相关的知识体系(概念库),并且能持续不断地完善。比如说,你是个后端专家,那么你能理解后端开发中的大部分概念,以及这些概念之间的关系。诸如于:
> **Spring Boot** 是一个可以用于帮助我们开发**微服务**的框架;**微服务**是一种基于服务的**分布式**架构风格/**架构模式**;**架构模式**是模式的一种,其中采用最广泛的是**设计模式**;**分布式架构**通过远程协议连接多个**部署单元**。
>
> 基于 Spring Boot 构建的应用可以是一个部署单元,通过**持续集成**构建**,**并**持续部署**到**容器化平台**上。
能知晓整个体系的相关**概念**,并清晰地知道概念之间的关系,再有一定的经验,我们就是入门级 “专家”。而后,一旦来了一些新的概念,我们还需要能将它们纳入到我们的体系中。诸如于最近在后端开发领域又重新火起来的 Cells-based architecture,它也是一种架构风格,同等于微服务架构。我们所能构建的是一个领域的思维框架,它可以帮助我们对所有的知识分门别类。
## 1. 寻找持续成长的动机
首先,我们要思考的第一个问题是,为什么我们要成为一个技术专家?
> 不管动机水准的高低为何,人们若能维持一定的动机水准,则不但能维持追求该目标的行为,也能维持心理上对该目标的渴望,直到人们知觉到该目标达成为止。 —— 维基百科
六年前,我参加过一个 Management 3.0(有兴趣的读者,也可以翻看《管理3.0:培养和提升敏捷领导力》)。虽然,这个培训确信了我不适合这个无聊的工作。但是,培训/书中介绍了一个 CHAMPFROGS 模型,它可以用来帮助我们寻找内在的动机。它由十种激励因素(**好奇心,荣誉,接受,精通,力量,自由,亲和力,秩序,目标,地位**),包括内在动机、外在动机或两者兼有的因素组成。(有兴趣的读者,可以翻看:<https: 11="" 2013="" intrinsic-motivators="" news="" www.infoq.com=""></https:> )
你也可以尝试一下,从上面的十个动机,按一、二、三的顺序,挑选出最与你匹配的动机。进而,你就可以发现你成长的动力在哪里。我记得多年以前,我的主要动机是好奇心、自由,其中有一个我已经忘了,估计也不重要了。
总有人会说:“hi,我成为技术专家的专家是赚更多的钱”。那么,问题来说,你如何定义多和少,怎么去衡量它们呢?对于打工人而言,你赚的钱多数时候,并不是靠你的能力决定的,而是你的行业决定的。所以,久而久之,将赚钱作为成长的目标,你会失去这种动力。因为,你的技术成长并不会从收入上得到回报。
## 2. 塑造整洁的编码习惯
整洁的代码意味着很多事情,你可以从《代码整洁之道》得到更多相关的知识。作为一个刚入行的程序员,在代码上充斥着大量的问题,诸如于:
* 无用的注释
* 注释的代码
* 混乱的代码风格
* 缺乏设计/重构的代码
* 缺乏自动化测试,导致大量的 `println` 或者 `console.log`
* 不会使用工具加速开发。如 IDE 快捷键、snippets、emmet 等
* ……
如果在工作一两年之后,你依旧还是这样,就需要警惕一下。基本的编程习惯都没有养成,离专业的程序员的距离就更加遥远。而这些简单的问题,大部分都可以通过 IDE 来帮助我们发现,如 Intellij IDEA 这一类的工具。
所以,我建议新手程序员应该优先考虑现代化的 IDE,从工具上花的钱,早晚会通过其它方式赚回来的。
## 3. 建立定位问题的方式
我们一直在说,程序员大部分是 ctrl + c/ctrl +v ,即 Copy and paste from Google/Stack Overflow/GitHub。但是呢,能做到这一点的程序员,本身并不多。学会使用 Google,是作为程序员的一个很大的门槛,而大部分人都跨不过这个门槛。另外一个门槛,便是访问 GitHub,大量的可学习的代码在上面。
从查看问题的角度来说,我们可以发现新手经常:
* 忽略到错误信息上显而易见的信息,如 error 等。
* 不会有效地看错误信息。只看最后的结果,或者截错图。
从分析问题的角度来说,我们还可以发现新手们:
* 不会去查看官方的文档。哪怕官方文档真的是最好的。
* 不懂得如何查看文档。
* 忽视从错误信息搜索,是最有效的手段。
* 不懂得如何使用**关键字**搜索。即采用相应的技术术语,如:Spring Boot JPA Query
* 不知道 GitHub issue 可以搜索
而在定位问题上,虽然对于新手有点难,但是依旧可以做一些尝试。诸如于通过 review 代码变更、回退,或者是自动化测试来帮助我们定位问题。
## 4. 学习既有的模式和最佳实践
对于新手来说,值得注意的是,我们在这一个阶段遇到的问题,大部分都是一些已知问题,往往可以搜索到资料来解决。大部分困扰你已久的问题,往往在书上,或者通过 Google 就可以得到这样的答案。
也因此,在多数时候,我往往会通常买书来快速熟悉一个现有的领域。没有什么能比,买知识更划算的知识。虽然说,互联网上也包含这些知识,但是搜索是需要成本的。对于编程来说,大量的知识已经被先辈们总结过。与其再自己汤坑,还不如直接买本书方便。所以,不妨去寻找一些书单,诸如于:<https: 121444657="" doulist="" www.douban.com=""></https:>
广泛意义上的模式是一个好东西,比如如何去分析问题、拆解问题等等。
你也可以多去搜索看看,新手程序员的建议。
## 5. 频繁性自我总结
不要把日报、周报视为自我总结 。这是的总结是指对于技术、模式等的总结,它可以是:
* 如何应用某个框架和模式的总结
* 如何一步步采用某种框架的总结
* 分析某个框架的原理的阶段性总结
* ……
编程生涯很长,我们使用过或者将使用的技术很多。新的技术层出不穷,绝大部分的新技术都是基于已有的改进。与此同时,我们学习过的大量有趣的东西,并不会在工作的时候用上,或者用到的概率很多。
而如果我们不去记录这些有意思的东西,通过代码托管网站或者博客的方式,那么我们再次遇到它们的时候,就需要重到再来。所以,可以多做一些总结,以便于将来使用上。
## 其它:专家的知识诅咒
也是好久没有接触毕业生,所以过程中陷入过**知识诅咒**的问题。即如果我们很熟悉某个对象的话,那么我们会很难想象,在不了解的人的眼中,这个对象是什么样子的。。简单来说,就是无法预知毕业生的平均水平,需要多次的解释,才能将问题解释清楚。
对于我的文章来说,这个问题也是由来已久的。只是对于我来说,要解决这个问题并不容易,也不是我的义务。博客一直在那,或许,多年以后,读者就能自行理解。
对于专业的程序员来说,也存在类似的问题。我们习以为常的内容,在一些新手看来,往往是无法理解的,我们也很难解释清楚。在解释的过程中,还有可能带入了更多的概念,导致新手程序员更加困惑。诸如于,我在解释一个几百 M 的文件提交到 Git 中,为什么会存在的时候,引入了 blob、索引等一系列的概念。这时候的效果反而不如右键 `.git` 目录查看一下大小,来得简单得多。面向开发者的网站,真的是认真设计过的吗?2022-07-03T12:18:07+00:002022-07-03T08:18:27.906565+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/time-to-design-developer-experience/最近几年,随着[技术产品化](https://www.phodal.com/blog/technology-product-marketing/)的流行,越来越多的公司(如云厂商、开源软件公司)将软件提供给市场,2D(to Developer)成为了一种炙手可热的商业 “风口”。所以,我们会看到各种面向开发者的网站以及各类的服务。
只不过,绝大多数的公司并没有考虑开发者们的体验,诸如于:
* 只需要在网站轻松点击三步,你就可以创建一个项目。呵,就不能提供个 CLI 一步到位吗?
* 在页面上拖拉拽就可以构建流水线。呵,就不能提供配置来修改吗?
* 我们提供了高级搜索功能,你需要选好你的条件,就能搜索。呵,就不能提供表达式和示例吗?
* ……
从传统的意义上来说,这种设计也没错,面向新手开发嘛!只是,这样的功能,新手用了一次之后还需要吗?一个有经验的新手,它需要的就是一系列更便捷的方式。
在这个关注于体验的时代,我们还能设计好面向开发者的网站吗?你们有考虑过开发者体验设计吗?
## 上半场:搜索框的体验设计
刚好,最近,我,一直纠结于一个搜索框的设计。所以,先让我们来看,两类类型的不同搜索框设计。
### 一个普通的搜索框:面向普通用户
因为不可描述的原因,我的博客一直在 AWS 上,大概快有七八年了。同样是一个搜索功能,相比于某些公司,AWS 在这方面就能提供一个更好的体验。通过 `Option` + `S` 就可以唤起搜索框,随后就可以到具体服务下的:**Top features。**
![](/processor/blog/images?file_name=2022-07-03T07:06:08.738Z.png)
具体的服务提供得好不好另说。但是,这种:
* 还有能用的搜索。更不用说有的云厂商,连搜索个 “AI” 都做不好?
* 适合于显示器的字体与布局。开发人员,谁没有个大显示器呢?
* 快捷键唤醒搜索。老程序员,谁没有过鼠标手呢?
当然,AWS 这垃圾服务器太贵了,大抵这就是智商税。在传统的 2B、2C 场景下,强业务方的公司里,业务领导决定了一切,他们看不出平平无奇的 Google 搜索框的背后花费了几亿,甚至于几十亿的成本,为的就是为几十毫秒。而在 2D(to Developer)的场景下,开发人员完全可以看出这方面的技术水平和能力。
### 基于代码的搜索框
最近,在开源软件 ArchGuard 里,我们实现了一个名为 Insight 的架构洞察功能的 PoC(概念验证)。在这次验证里,我们参考了 SourceGraph 的搜索设计,使用 Monaco Editor 构建了这个功能的搜索框。结果,如下图所示:
![](/processor/blog/images?file_name=2022-07-03T06:30:55.824Z.png)图中的 `field:dep_name == %dubbo% field:dep_version > 1.12.3` 即为搜索框。与搜索功能相比,它更像是一个带有搜索功能的输入框;然而,搜索的方式是通过表达式的方式来进行。而为了更友好地支持**表达式**的输入,表达式变成了一个代码编辑器。只有在代码编辑器里,你才能自由地去书写这种形式。
如果你有一定的前后端开发背景的话,那么就能评估出这样一个功能的工作量:
* 语法解析、高亮的输入框。在 Monaco Editor 上自定义语言、语法解析、高亮、自动填充等。
* 自动关联上下文。
* 表达式设计。
* 表达式合法性校验。
* ……
后期,也非常有益于扩展,加一个新的字段什么的,完全不考验设计人员,也不考虑开发人员。于是乎,在考虑了工作量和之后,选择了这样的一个方式(PS:在领导的角度,下面的方式看上去就非常高级):
![图来源于网络](/processor/blog/images?file_name=2022-07-03T06:38:15.617Z.png)
看上去还不错,如果不添加新的字段 —— 但是,添加新的字段几乎是 100%。也许是开发者的体验都让狗吃了,面向领导服务才更重要 —— 我见过其它更迷之设计,只是因为领导觉得代码化(配置化)的体验对开发者不好。所以,总有公司会比 K8s 的配置化做成了表单……,不是吗?
## 下半场:“狗” 看了都直摇头的文档
首先,文档都是给不懂的人写的,包括写文档相关代码的人。其次,文档都是写出那些出现 bug 的人,只要不出 bug,我就不需要看文档。
1. “啊,这个文档不错,良心地给了个代码示例”
2. “等等,这是一个图片”
3. ”嘿,我去找个图片识别工具“
4. ”啊,这个图片识别工具需要看一下文档“
好惨。
### 祈祷过程不出错
每次在使用新的工具的时候,我总会期待我不会在过程中失手。然而,有时候会在最后一步,有时候它是在第一步。所以,**如果你的工具足够稳定,你就不需要任何文档**。
而在用了文档之后,我选择再试一次,并祈祷不出 bug。
以至于我至今怀疑,国内的新手程序员不看官方文档的习惯,可能源自于国内的公司给新手们一个印象:官方文档非常不靠谱。
### 让人迷失的链接
在考虑平稳开发者体验与文档体系的时候,我们通常会这么考虑:**有意义的 URL 命名**。以便于构建一个网站-代码-文档相关联的体系,即代码出错可以找到文档,文档可以链接到网站
然而,使用国内的云服务时,我经常丢失在文档的海洋里。于是乎,我会有几种方式解决:
* 自带的文档搜索。我没见过,几家公司能做好的,因为自己人都不用。
* Google / 百度搜索。说实话,在这一点,百度还是比 Google 好用,建议你使用另外一家的服务。
* ……
作为一个中老年人,我保存了这个地址在我的记事本里,以便于下次打开的时候使用。于是,下次打开记事本,**狗看了都就直摇头**:
* /document_detail/1364/53045.html
* /document_detail/1234/45678.html
* /document_detail/13432/23445.html
地址是哪个来着,微服务框架是哪一个链接来着?咦,这个链接怎么失效了?要怪就怪微信不支持 URL preview。年轻大就是吃亏。
### 文档还需要体验吗?
类似的一些槽点就诸如于《[文档工程体验设计:重塑开发者体验](https://www.phodal.com/blog/documentation-enginnering-experience-design/)》所描述:
* 文档代码不同步。即文档的 API 变化可能落后于代码,导致 API 与文档出现不一致。
* 频繁的 API 变更。API 变更时,文档需要手动进行更新,不能自动化同步。
* 概念不统一。对于同一个概念,文档的不同地方描述不一致。
* 重复的文档块。文档需要重复引用某一部分的文档,不能像代码一样引用。
* 代码无法运行。按照文档的步骤下来编写的代码、复制的代码,是不能运行的。
简单来说,就是人们从未把**文档当成一个工程**来考虑。当你把一个支撑域当成核心域的时候,那么剩下的就自然而解了。
## 中场休息:UI vs CLI
我觉得这又是另外一个很有意思的话题:如果我们都用 CLI 的话,那么谁来考虑 UI 的问题。不过,我反正是不想继续吐槽了,前面都做不好……。
当然,在设计 CLI 的时候,还要考虑这个问题:
* CLI 的文档体验设计。又绕回去了
* 服务更新时,是否提供自动迁移能力?
* CLI 的更新机制。
* ……
在不考虑将开发者的体验作为第一优先级时,我觉得没有人会去考虑这些。要怪就怪程序员都锻炼得太刁钻了 —— 谁让他们就是写体验的人。
## 其它
咦,过头来看,什么是开发者?什么是开发者的体验设计?
所以,我觉得程序员都应该在显示器下面放一个手办,不是用来祈祷上线成功,而是用来祈祷自己不用写文档 —— 这样就不会被骂。2021 节点:赶不上变化的计划2021-12-31T15:52:03+00:002021-12-31T15:52:34.508061+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/node-2021/计划赶不上变化,大抵说得是我对 2021 的总结。过多的变化,导致了不少的「计划超期」,也就是所谓的 ”失败“。除了事物本身的复杂性,还可能在于过多的计划导致的。复杂性在我们试探之前,是无法难以正确估算的。而过多的计划,便是个问题。
2020 年的一些想法:
1. 云研发体系构建。今年:理论差不多完成了,需要一个示例。
2. Datum 语言。今年:暂时搁置了,找不到特别的高点。
3. 构建下一个知识体系构建。今年:梳理了《开发者体验:探索与重塑》
4. 设计。现状:暂停了画画
5. 构建开源梯队。今年:开始创建 Inherd 团队
所以,2021 的总结大概就是上面这些了,哈哈。
## 编程:研究为主
在技术上,做了诸多颇有意思的研究,也算是颇有体会,比较有代表性的应该就是《[调研技巧(上):以『时间旅行调试』为例](https://www.phodal.com/blog/arts-of-research-time-travel-debugger-as-example/)》、《[领域特定语言设计技巧](https://www.phodal.com/blog/step-by-step-domain-specific-language-design/)》。所以,在技术和开源也主要是围绕着几个方面展开的。也因此,我读了一系列的论文,也学会了一些快速阅读 + 索引的技术,不过,也由此我发现我缺乏一个有效的方式来管理这些论文。
从方向上来说,主要是围绕于云研发体系的 IDE、DSL 相关的研究。这些相关的 DSL 项目有:文档-代码同步工具 [Writing](https://github.com/inherd/writing/)、架构设计 [Forming](https://github.com/inherd/forming)、架构守护 [Guarding](https://github.com/inherd/guarding)、反向模型生成 [Modeling](https://github.com/inherd/modeling)、低代码设计语言 [Unflow](https://github.com/inherd/unflow)。另外则是围绕于云研发体系的 IDE 部分:[Water](https://github.com/phodal/water/) 架构和 Uncode 概念性 IDE。
除了这部分,还有用于自动化分析与建议的工具 Coco,可以实现轻量级架构可视化。
所以年底的时候,构思了开源知识管理元框架 Quake,作为明年的主要方向。
## 创作
今年,依旧写了一系列的文章,所以倒是没有什么特别的,比较推荐的就上述的两篇研发相关的文章。还有一些文章也比较推荐:
- 《[元素建模:探索建模的要素](https://www.phodal.com/blog/elemental-modeling/)》
- 《[文档工程体验设计:重塑开发者体验](https://www.phodal.com/blog/documentation-enginnering-experience-design/)》
- 《[“内源”文化优于中台思想](https://www.phodal.com/blog/innersource-over-zhongtai/)》
- 《[模式的模式:从设计模式到元模式](https://www.phodal.com/blog/patterns-of-patterns-meta-pattern/)》
- 《[团队拓扑:在云原生时代,如何定位自身与团队?](https://www.phodal.com/blog/cloud-dev-define-develeoper/)》
- 《[云原生转型:规模化演进与文化思考](https://www.phodal.com/blog/path-to-cloud-native/)》
- 《[技术产品化运营](https://www.phodal.com/blog/technology-product-marketing/)》
对应的一个体系化的产出是:《[开发者体验:探索与重塑](https://github.com/phodal/dx)》。它起源是在 2019 年时,在经历了多个低代码前端项目的售前,以及一个低代码项目的技术实践强化,发现国内的 IT 企业缺乏对于『开发者体验』缺乏系统性的思考。便想着结合自己的项目经历、社区经验、国内外实际情况等,编写一个简要的开发者体验电子书。
对于 2022 年来说,同样的也需要有类似的体系化输出,以帮助自己更好地理解某个领域。
## 社区
社区,大抵是我在 2021 做了比较多的改变,试图去构建一个 “自由” 的团队/社区。
### Inherd 开源小组
年初,构建 Inherd 开源小组时,我的想法是以**开源项目为核心**,构建一个围绕于数字化隐私和底层开发的开源兴趣小组。但是呢,这是一件颇有难度的事情:以代码参与到贡献中是存在一定门槛的,尤其是诸如于 Coco 这样的代码分析性质的项目。所以,Quake 这样的更为简单的项目,可能是一个可行的方向。另外一方面,我们也可以尝试性探索其它的可能性。
### 社区活动
从习惯上来说,我偏向于是以写作 + 开源的方式来贡献到社区上。我正在尝试性参加一些对外的演讲和分享活动,这里的尝试是指**如何更好地平衡时间**?诸如于,今年在编程语言社区上做的分享《DSL 在未来软件研发体系:云研发中的意义》,还有其它的一些分享。只是因为技术咨询的性质,地点变得高度不确定:可能今天在南京,明天就在北京,后天在某个地方了。时间就变得更加可贵了,要有时间来陪伴家人,要有时间来写代码,还有写作。
所以,在下一年里,我还会做一些尝试 —— 当然只限于我感兴趣分享的新话题。
## 2022?
我有一本书在今年写了 1/3,然后搁浅了,但愿明年能写完。
再次重构知识管理体系。
继续构建 Inherd 团队和开源项目。
尝试使用新的语言练手。2021 年技术书籍推荐 ——『Phodal 精选』2021-09-06T12:49:49+00:002021-09-06T12:50:17.889553+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/book-recommend-2021/说来也奇怪,每次我想推荐一些书籍的时候,都是在奇数年,也都是在 9 月份。比如说,《[2017 年的编程书籍推荐](https://www.phodal.com/blog/it-book-recommend-in-2017/)》、《[2019 年的书籍推荐](https://www.phodal.com/blog/2019-phodal-book-recommend/)》。大概是到了秋、冬的时候,温度下降了,比较适合我进行一些思考和编程,所以我就不怎么在这个季节看书了。另外,从我的感知来看,我怀疑到了四季度新书也越来越少了。
今年推荐的书籍里,一部分是技术书籍,一部分是 20~50% 的技术书籍,也就是说和技术相关。我本想推荐一些不错的非技术书籍,但是呢,看得少,所以也推荐不了几本。
推荐的这些书籍,主要是我的兴趣和工作相关,主要是以兴趣为主,所以这 10 本书里主要会集中于:
- 编程语言设计:《C++ 语言的设计和演化》、《JavaScript 二十年》
- 数字化转型:《EDGE:价值驱动的数字化转型》、《数字化转型:企业破局的 34 个锦囊》
- 软件开发协作:《用户共创:社区赋能产品实践手册》、《高效能团队模式:支持软件快速交付的组织架构》
- 软件工程:《活文档:与代码共同演进》
- 软件架构:《软件架构:架构模式、特征及实践指南》、《解构领域驱动设计》
- 嵌入式:《TinyML:基于 Tensorflow Lite 在 Arduino 和超低功耗控制器上部署机器学习》
我比较懒,还穷,所以一般不怎么阅读英文版本的书籍。
## 《C++ 语言的设计和演化》和 《JavaScript 二十年》
过去的两三年里,一直在研究领域特写语言以及编译器相关的领域,所以对于编程语言的历史比较感兴趣。说实话,这两本书到现在我没怎么看,因为我的编程语言计划搁置了一段时间。不过,我翻了几页,还是觉得挺好玩的,看看语言的一些八卦史。
《C++ 语言的设计和演化》是在 2020 年再版的书籍,在那之前分别在 02 年、12 年出版,当时我还年轻。这本书讲述了 C++ 的历史和发展,C++ 中各种重要机制的本质、意义和设计背景等,作者在讲相关的设计旅程,非常不错。
《JavaScript 二十年》则偏向于讲述 JavaScript 的相关历史,口吻上没有 C++ 那么有意思。
## 《高效能团队模式:支持软件快速交付的组织架构》
从书的标题上看,这不像是一本技术书籍。但是这本书的作者是两个 DevOps 方面的专家,它们以团队作为视角来优化架构,即基于康威定律的团队侧来重新看待问题。除此,这本书的中文标题中没有体现出英文版的核心『Team topologies』(团队拓扑),又是一个失败的标题。大抵是为了销量,而忽略了其内涵所在,颇为可惜。
在我的上一篇文章《[团队拓扑:在云原生时代,如何定位自身与团队?](https://www.phodal.com/blog/cloud-dev-define-develeoper/)》中,详细展开了阅读了这本书之后的一些思考。
我就不做更多的介绍了,可以看看原书,又或者是阅读我的思考~。
适合人群:Tech Lead、Manager。原因是,如果不能站在组织的层面来考虑问题,那么能收获的东西就会比较有限。
## 《活文档:与代码共同演进》
这本书介绍的活文档的观念,和我最近几年做的事情非常相似:如使用定制化的 markdown 作为知识存档工具、使用定制化的 DSL 取代图表、拓展 Gherkin 以承载更多的知识…… 简单来说,传统的文档难以进行版本化管理,所以我们需要更灵活的方式让文档活起来。 当然了,我并不建议你们用语雀这样的工具,知识应该以 Git 进行管理,代码化,才好演进与重构。
推荐它的主要原因是:**理念**。它是一次关于文档代码化的讨论,我们可以从中重新思考文档化的策略。审视一下,我们所在组织的文档流程,会发现期中的文档都是以一定的模式存在的。既然是模式,那么它就可以模板化,部分内容就可以代码化,如我先前构建的架构决策记录工具:adr、DevOps 知识平台:Ledge 就是相关的尝试。
## 《软件架构:架构模式、特征及实践指南》
这本书的英文名称是:《Fundamentals of Software Architecture: An Engineering Approach》,简单来说,就是『软件架构基础』,吐槽中文书名就到此结束,毕竟利益相关 —— Thoughtworks 出品。PS:吐槽书名,一直是我的惯例,如我的书的标题也都不太好。)
从英文书名的定位可以看出,这本书定位的是架构入门 + 架构模式总结。这本书有一大半的内容是在讲架构风格,这部分内容定位的也是架构基础。另外一些则是关于架构思维和架构师如何成长。从内容上来说,我觉得值得每个人去阅读(资深的人可以推荐给其它人看看),正如书的推荐语所说:“我多么希望在我职业生涯的初期就能看到这本书”。
## 《TinyML:基于 Tensorflow Lite 在 Arduino 和超低功耗控制器上部署机器学习》
我只是喜欢这本书的标题。除此,作为一个嵌入式行业出身的软件开发工程师,我还是会缅怀一下这个行业的。
这本书的标题给我提供了一个造轮子的新思路 —— 如何更好地进行降维打击。
所以,我买这本书的理由是:1% 的灵感。当有一天,我在书架上看到这本书的时候,可能会有一些新的灵感。
也因此,我并不建议大家买,哈哈哈。
## 《EDGE:价值驱动的数字化转型》 和《数字化转型:企业破局的 34 个锦囊》
数字化是未来的趋势,毕竟国家战略。利益相关,都是 Thoughtworks 出品的书籍。我好像不得不看 —— 万一有人问我书上写的是什么,那么我要怎么回答。不过,总的来说,两本书都不错。
EDGE 是在 2020 年 9 月出版的书籍,EDGE 是一整套用于实现数字化转型的工具,它快速、迭代、自适应、轻量级并且基于客户价值驱动。书中有个重要的模型:基于精益价值树(LVT)和度量标准(MoS)的价值驱动模型。这个模型蛮有意思的,我后来把它应用到了架构设计上了。
适合人群:Tech Lead、Manager
《数字化转型:企业破局的 34 个锦囊》则偏向于面向管理层了~,我只是顺便帮带一下货。让我吐槽一下《数字化转型:企业破局的 34 个锦囊》这本书的书名,34 个锦囊没有体现在目录上,只在最后几页体现,……。
适合人群:Manager
## 《解构领域驱动设计》
关于 DDD(领域驱动设计)这一话题而言,最好的书依旧是《领域驱动设计:软件核心复杂性应对之道》,而这本书是一本以模式语言组织的书籍,所以它并不适合作为入 DDD 的入门级书籍。而且,从个人的角度来理解一直觉得这本书的大纲组织模式,是以重构作为内核来驱动落地的,所以并不是那么好实施。
先前,我推荐的关于 DDD 的入门级书籍是《领域驱动设计模式、原理与实践》,只是呢,这本书的翻译不好。现在呢,我们有一个更好的选择《解构领域驱动设计》。书中提到的领域驱动设计统一过程,也是一个不错的方法论。
## 《用户共创:社区赋能产品实践手册》
本书是一本讲述如何实施《[技术产品化运营](https://www.phodal.com/blog/technology-product-marketing/) 》的书籍,从某种意义上也是在讲技术社区如何运营。
我推荐这本书的主要原因是,在需要帮助公司做一些开源相关的运营,也是颇为头疼的。所以,有这样一本书的话,相关的工作就可以交给别人做了。
另外一个原因是,有越来越多的公司需要去推广他们的技术产品,这本书也能提供一个不错的指导。
## 小结
写书评是一件极为累人的事情,把书看完,再做一些概要总结;买纸质书籍也很贵,还占地方,搬家的时候也很重。过去两年:回顾第二个 5 年的计划2021-07-21T12:04:50+00:002021-07-21T12:05:31.603424+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/review-five-years-plan/两年前,我已经工作 5 年了,我构思了一个从 2019 ~ 2014 年的工作上的计划。如今,两年过去了,我又得拿出这个计划来做一些回顾。
PS:详细的计划内容,可以看《[未来 5 年:构建第二个工作 5 年的计划](https://www.phodal.com/blog/build-second-5-years-of-career/)》。
从总体来看,一切似乎是按目标进行的,但是依旧会有一些差异。我想着把这部分的内容写出来,做一次快速的回顾,希望对于大家能有所帮助。
## 一个小的总结
在制定计划的时候,时间在 2019 年 7 月 8 日。当时我还在 Thoughtworks 交付团队,恰逢组织结构变更,本来是归属于华南 unit,又来因为 rebase 的缘故划到了华东 unit。
后来,几个月后,“机缘巧合”之下,我正式从交付业务线转了咨询业务线,时间在 2019 年 10 月 18 日。
再回到第二个五年计划上来,在计划的初期,除了一个伟大的远景。基于这个远景,我还有了一些新的定位:
- 创新者(Inventor)
- 技术/架构倡导者(Technology/Architectue Advocate)
- 开发者教练(Developer Coach)
基于这几个愿景来看,每一个部分都走出了它们就应有的一步。从时间上来看,在剩下的三年时间里,它们大部分是能完成的。
## 回顾远景
两年前,我设定的**远景**是:未来 5 年,我希望我能再给整个行业带来一些变化,并且**不限于国内,走向国际**。
从现有的情况来看,带给国内的效果还是勉强还行。但是国际化的这事嘛,一点儿也没做。从过去两年的中美形势来看,我在犹豫后期要不要继续往这方面努力。另外一个原因是,我从交付转到咨询的时候,要适应一下咨询团队的生活。所以,我在过去两年的主要精力,还是在相关的尝试上。
这一系列都是存在变数,未来还有 3 年的时间,至少在短期(半年内),我是暂时不考虑国际化这一事,我还要忙于别的事务。
### 回顾定位:创新者
回顾一下在这个定位下的几个目标:
1. 多个领域的技术专家,以在不同的领域里 Copy / Paste 相关的思想。如过去的微服务 -> 微前端,APP 的 MVP 架构到前端的 MVP 架构。
2. 全新领域的探索者。新的领域,可以带来新的可能性,参与新领域的开拓。
3. 革新者(Innovator)。Innovation,拥有将创新的成果落地的能力
4. 创新者(Inventor)。Creativity 创造一些新的技术成果。路还有点长,但是可以尝试去做。
过去的两年里,我一直在抽身于《云研发》相关理论体系的建设。围绕于此,我开始构建在编程语言的能力,并将领域特定语言应用于一系列的架构上的抽象。从过程上来看,在 2、3、4 上勉强还算是有一个不错的进度,从产出与现状的情况来看,它能符合我对于目标的设定。
对于 1,可能无法产生预期的结果。在接触了更多的资料之后,我发现它存在一定的难度。因为,技术是一种通用的能力,如何界定多个领域是个问题。另外一个问题是,在有时间读了更多的书之后,我发现我对于领域的理解有一些失误,只能再探索一下看看了。
### 回顾定位:技术/架构倡导者
继续回顾一下这个维度下的四个目标:
1. 走向国际化
2. 让人起而行动的布道师
3. 探索更好的技术架构,对架构进行抽象
4. 在组织层面影响架构采用与实施
从现有情况来看,对于 1 ,也就是国际化这个问题,估计得放到下一个五年了 —— 要忙的事情更多了,可能没有时间去提升英语能力。至于 2 和 3 来说,我觉得它能符合我的预期目标。不过,对于让人能起而行动来说,我觉得我还需要提升一下。
至于 4,我觉得可以一个阶段的主要目标,探索一下如何在影响更高层的技术官僚。
### 回顾定位:开发者教练
自我转入咨询团队之后,我好像自动发展了这方面的能力了。依旧看一下这方面的四个目标:
1. 帮助开发人员构建更好的软件系统
2. 快速指导他人的技术发展
3. 成为有效地发展他人的 Coach
4. 构建开发者团队
从目标的情况来看,除了第 1、4 个目标,效果还不是很明显。对于目标 4 来说,我觉得只能说我在努力去做一些尝试。但是呢,不在 office 之后,要在这方面努力地话,依旧需要更的尝试。
## 计划与职业生涯
回过头来看,我只是想建议大家试着去做一个更大的计划。
诸君,或许都是和我一样热爱技术的人。热爱者,则会依附于技术构建路线图。它不因你的工作单位有所变化 ,也不因你的薪资有所办法。只是非常单纯的技术,不涉及其它。
如果你热爱于技术,那么你需要平衡收入、技术与家庭多个维度,在其中做一个合理的平衡。平衡是一件非常难的事情,你希望提高收入,以便于提供给家人更好的环境,这样一来你可能会牺牲一些技术成长的空间,还有陪伴家人的时间。如果你决定提升技术,还要陪伴家人,那么你在提升收入的空间就比较有限。
所以,有空再考虑一下自己的职业生涯。在考虑更高的收入之时,你需要考虑一些额外的因素:能力成长与兴趣发展。从价值的曲线来看,能力最终会与收入相匹配的。你会因为跳槽获得更高的收入,但是如果不是一个更好的能力成长机会的话,那么长期来看你可能会失去更多。
所以啊,为了你的职业生涯,你需要一个计划
## 其它
另外一个难题是,我们还要考虑技术的边界在哪里?如果技术的未来是可见的,那么我们是不是需要一些新的变化?编程语言的被淘汰:选错语言毁终身2020-11-02T12:38:22+00:002020-11-02T06:38:57.345319+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/deprecated-language/在我当前所在项目里,其中的某一个子系统是用 Groovy 中的 Gradle 插件。Groovy 作为一个运行在 JVM 上的脚本语言,天生具有胶水的特性。加之,它支持 DSL 与其程式的简洁语法。嗯,如果不考虑性能问题,这真的是不一个不错的语言。
**可真的是如此吗?**
开始之前,我再次 FBI warning 一下:
1. 关于编程语言的讨论,并非能真实,都存在或多或少的个人偏爱因素。因此,文中的某些观点或许会有些偏颇。若是不正确又或者是出入较大,也希望大家能指正。
2. 这里的场景主要是基于**团队协作**的场景之下讨论的,而非个人项目,又或者是小项目。也就是说,只有团队协作时,才会出现的问题,才会出现各种讨论。
3. 说起这一点我也是有个人偏好,如业余开发选各种语言,而真正做项目的时候,选的语言便是 Java;业余开发用 React、Stencil.js,在公司做项目的时候,还是 Angular 大法好。
## 引子 1 :编程语言的读与写
我们都知道,**编程语言是写给人看的代码,写机器运行的机器码**。所以呢,对于编程语言而言,我们会有一个简单的判别标准,即它的**读与写**。从使用体验上呢,我们可以分为:易读易写,易读难写,易写难读,难写难读,这么四类的语言。
为了贴合文章的主题,我大概对我在项目上用过的 JVM 语言做了一个分类。(PS:真实情况下,差异没有这么大。)与此同时,由于每个语言的使用场景不一样,我们并不考虑诸如于性能等问题。
| 难度 | 易写 | 难写 |
|--------|---------|-------------|
| 易读 | Java | Groovy |
| 难读 | Kotlin | Scala |
简单说明一下 (笑,我们并不讨论他们的优点。例子中的 Kotlin 不太适合,只是我暂时没有在项目上用过其它 JVM 语言,也许 JRuby 就不好读了):
Java 语言嘛,大家都懂,又好读又好写,所以 Java 程序员便宜。
Groovy (Gradle 所采用的 DSL 语言)难写的地方在于,文档少、语法糖导致IDE 支持差(相对而言)。事实上,它也不是那么好懂,在 IDE 支持的情况下,要用碳基脑做个类型推断。
Kotlin,如果已经熟悉 Java 或者其它语言的话,写 Kotlin 并不是一件难事。这件事情难就难在阅读别人的 Kotlin 代码,可能会有点费劲,除非你有良好的 IDE 支持——它的亲爸爸可能是 Jetbrains。离开了 IDEA,找个扩展(extension)都得找半天。所以难度总体上还是不难的,只是相对难读一点——因为语法糖。
Scala,早期的某个项目,我几个月后看不懂几个月前写的代码。
而如上所说,对于语言每个人是有偏好的。所以,这里依旧是我的一些个人观点。 我也并非这方面的专家,只是从个人阅读开源代码和编写相关代码的感受来说的。
## 引子 2: 适用领域与流行应用
谈及编程语言,我们要讨论的是另外的另一特质:适用领域。如我们熟悉的:
- Golang 背靠云原生和 Google
- Python 是科学家们的偏好,毕竟不是以代码为生。
- JavaScript 是交互方式发生了变化
- Ruby 是 Rails 框架,所以流行开了。
- Java 用于企业编程,因为程序员便宜
而诸如 Rust 这样的小类语言,还没有正式有一个能发扬光大的场景。
## 引子 3 :编程的快乐,先写得爽
有一些语言能让你拾起编程的快乐,比如 Ruby,但是也能让你不想去维护代码——让人又爱又恨的 Method Missing,可以让你搞起元编程。也能分分钟让你看不懂别人写的代码。如果没有文档的话,那么我觉得你不会再看了。
又比如说,操作符重载也是一个让人写的代码更加直观。嗯,再重载一下**赋值操作符**,是不是非常爽。
对于快乐来说,维护性那是以后要考虑的问题。
## 编程语言的被淘汰
在项目上经历了惨痛的 Groovy 开发大型项目的经验后,我和我的同事们一致觉得这是一门可能被淘汰的语言。主要原因有这么几个:
1. 可维护性丢失
2. 缺失更好的 IDE 支持(相比于 Java 之类的)。说白了就是开发人员写起来不爽。
3. 在最广泛的场景之下,可迁移语言出现(如 Kotlin Script)
如果你还想把编程语言的一些缺点考虑一下,那也是可以的。
### 可维护性丢失
这也并非是语言本身的问题,而是语言应对大型项目时,将会遇到的一个挑战。对于大型项目而言,自由灵活的语法糖会带来大量的问题。而随着项目的进一步扩大,保持同一套代码风格容易,而要使用同一套语法越来越困难。如同样是声明类型,有的用具体的类型,有的则是用 `def` 或者是 `var`。
### 缺失更好的 IDE 支持
嗯,如果你习惯了用 IDEA 对 Java 代码进行快速的重构之后。而与此同时,你并不能使用相似的方式来对你的 Groovy 代码进行重构。你们就会慢慢陷入了一个循环,既然有一个更好的语言,为什么我们不去使用它们呢。
退而求其次的,为了使用 IDEA 的高级功能,如重构。我们开始将代码中的 `def` 转换为具体的类型。
### 可迁移的语言出现
而其实上面两个问题,并不是这个语言的主要问题。毕竟,对于小的项目来说,IDE 和可维护性支持都不是问题。
过去,我们根据 Gradle 官方文档,使用 Groovy 来编写 Gradle 插件。而有一天,Gradle 官方文档同时提供了 Kotlin Script 的支持。
这就相当于是,上帝真的抛了个橄榄枝给你。你可以同时拥有更好的 IDE 支持,更好的可维护性。同时,还可以快速地迁移过去。为什么不呢?
### 其它
与之相似的一个例子便是 JavaScript 和 TypeScript,但是浏览器运行的是 JavaScript。所以,JavaScript 并不能这么容易被取代。
## 结论
有没有可能出现一个兼容所有语言的语言?六年之后:回到底层编程2020-08-13T05:01:08.286348+00:002020-08-13T05:01:08.323996+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/back-native-programming/一个月以前,我加入到 ThoughtWorks 已经进入了第 7 个年头。我本来不想再花时间写一篇相关的文章。只是呢,最近发生的一些事情,或许和每个 IT 人息息相关。我便想着,再花点时间思考一下:我们的行业以及行业背后的 IT 体系。
从某种程度上来说,我更可能是在弥补一块重要的短板 —— 一来,不是从事相关的领域;二来,平时的开发中真的用得少。
## Web 开发的核心:CRUD?
在我先前的文章《[项目初期的最优技术实践](https://www.phodal.com/blog/short-time-project-best-practise/)》里,我把产品开发的生命周期分为了:
1. 技术准备期
2. 业务回补期
3. 成长优化期
从技术维度来看待的话,我们会发现随着时间的推移,整个项目对于人能力的要求会越来越低,直至大家都变成 CRUD、Copy/Paste 程序员。唯一经常还被提及的难点就是:对于数据库的各种优化,又或者是对于遗留系统的迁移,又或者是对于某个开源软件的优化。
所以啊,我们听过别人提及一个明确的大公司技术发展路线:成为一个技术 + 业务专家。回过头来看,事实也是如此 —— 在大部分的公司里,成为一个技术专家,是一个投入大收益低的事情。对于个人来说,不一定有足够的回报,不一定有用武之地,不一定能找到相匹配的工作;对于组织来说,难以创造更高的价值。
可是呢?对于多数人来说,他们真的还是技术专家吗?如果不写代码的话,他们只是一个**技术决策者**,而技术决策者和技术专家是两种不同的发展路径,只是他们做的某些事情是重叠的。
所以,在 CRUD 之外,我们的一个侧重点还在于对基础设施的优化,而这些基础设施大多数是开源的。
## 开源是否在扼杀技术创造力?
自由软件和开源软件运动,解放了我们在整个行业的生产力。过去,我们关注于如何实现某个功能,现在我们可以转而关注于某个业务的具体实现。
不可避免地,在国内的一些大公司里,受过训练的、编程能力极强的程序员,变成了一个会写代码的业务专家。他们带领着大量的**平均水平**一般的外包程序员,一起编写着业务价值巨大的低质量代码。
这一切看上去似乎非常合理,但是行业总体的创造力在下降。特别是,如果中国的 IT 公司在开源领域的影响力过小时,整个市场都将充斥在水平一般的程序员。
所以,这样一来,我们又不得不考虑一个问题,开源是否在不断降低组织的技术创造力?对于个人来说,可以通过学习造轮子来提升,对于组织呢?
## 阅读源码的本质:解析字符串?
这一年里,受项目及公司大佬的熏陶,我开始寻求以半自动化的方式来理解代码。这其中的一个过程产物就是 Coca,以及 Chapi。往往复复的练习之后,对于类似工作的自动化,有了更充足的把握。
在机器执行一段代码之前,我们要进行一系列的前端转换:词法解析、语法解析、语义分析、生成中间代码,随后再交由编译器后端处理,直至目标程序。而对于人类来说,我们理解的过程是相似的,稍有区别的是,生成的中间代码是人类语言。
而对于一个新的项目来说,我们要做的就是把理解汇制成架构图,又或者是它们之间的调用关系。于是,Coca 和 Chapi 就是用来做这部分工作的自动化 —— 解析字符串。
所以,我一直想写一个工具,用于自动化生成不同语言的代码结构。只是吧,一直缺乏一个明确地用户场景,理论有了,技术有了,就差有人买单了。
## 可执行的背后:二进制?
起先,因为在完成之前的一个 Todo 列表,我便开始折腾写一个 TLV 编码、解码器。TLV,即标识域(Tag/Type)+ 长度域(Length)+ 值域(Value),它是用在通信协议的一种编码格式。
随后,我便开始尝试解析 Java 生成的 class 文件,嗯,我发现做的事情和 TLV 解析,又或者是 Coca 做的语法解析有些类似。Java .class 文件毕竟有固定的格式,所以反而比直接字符串处理更加简单。
紧接着,我继续扩展对于 Java 虚拟机的理解,尝试模仿造一个简单的 JVM,理解整个 Java 应用是如何运行起来的。
直到了,我发现有一个新的领域:Android 对于 Java 二进制的各种骚操作:class 转换为 dex,对 dex 进行优化,dex 转 oat 二进制……。
生活就是这么充满乐趣。
## Go、Rust 成为编程语言的新希望?
作为一个浪漫的程序员,我们无不时刻想着创建一个新的语言/DSL,以丰富我们的业余生活,调节一下 copy/paste 的乏味感。
在陆陆续续地学习 Go 和 Rust,并用它们创造了一系列的工具。我对于创建领域特定语言的事情,又有了一定的感悟 —— 适合的时机,适合的场景,便容易产生适合的语言。
特定的语言解决某一类特定的问题。当然了,新的编程语言还需要有一定体量的公司才能支撑起来。不过,领域特定语言就不需要了,你想干些什么,你就可以轻松上手了。
## 回到底层编程和基础设施
所以,既然大部分 Web 开发都是重复性 CRUD 的。那么回到底层编程、系统编程,偶尔造造系统软件,也是相当的不错。特别是当前,整个市场的大环境对于基础设施的需求,一直在持续不断地提升。
创造和学习是一种乐趣。
嗯,我打算业余生活写写底层代码,进行进行系统编码。微服务治理:自动化分析方式2020-08-10T12:21:20+00:002020-08-10T08:21:40.269108+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/microservices-auto-goverance/关于『设计一个微服务治理的工具』这个想法,我已经酝酿很久了。但是,你懂的,又是因为种种原因,我搁置了蛮久了。最近,刚好因为在研究『架构适应度函数』,所以,我有了一个新的想法。微服务架构治理,看似和架构适应度函数并没有啥关系。但是,我设想的是一个用于『微服务治理的架构适应度函数』。
你可以把它想象为一个用于帮助更好开发微服务应用的工具。顺便一提,因为手头上并没有这样的场景。所以,我先把我的相关思路记载下来,方便于后续集成。而且大部分功能已经在 Coca 中实现,我会将部分的功能再交由 Coca 来实现。如对于,数据库的自动化分析 —— 已经有 Tequila 进行了大量的自动化。
## 微服务粒度适应度函数
对于微服务架构来说,最令人头疼的一个问题就是微服务粒度。从最源头上,我们应该遵循『两个披萨团队』这个定律,即:
> 单个服务的设计,所有参与人从设计、开发、测试、运维所有人加起来 只需要 2 个披萨就够了。
但是,事实上从国内大中小公司的实践情况来看,并非如此。往往是一个团队维护了超过其自身数量的微服务,即 6 个开发人员可能维护了 8 个微服务。
大家常犯的一个错误是:通过技术维度而非业务维度划分微服务。关于这部分的自动化,我暂时找不到头绪。但是,我们可以判断两个微服务是否可以合并:即**基于 Git 日志的微服务粒度合理性分析**。
- 服务提交人数。通过 `git log` 来查看单个微服务的提交情况
- 变更频率。寻找多个模块之间,是否存在大量同时变更的情况
- 需求关联度。通过识别提交信息规范,来识别多个微服务、模块、类是否存在经常同时变更
- 前提:匹配提交规范。
在这个时候,我们只需要使用和 `coca git` 类似的解析函数,就能达到类似的效果。
## API 的适应度函数
在 Coca 中已经内置了 API 分析相关的功能,可以支持识别 Spring 的 API 注解,以及服务声明的 API 方式,同时分析调用关系等等。所以,我就不需要开发一个这样的功能了,只需要稍微完善一下,补充一些分值情况。
对于 API 设计来说,这个工具要做这么几件事:
1. API 命名的规范。如**不一致的命名方式**
2. 参数合理性。如过多或者过少、是否不应该出现在 URL 中。
3. 是否符合 RESTful 规范。如 URL 中不应该出现 `get` 和 `post` 等字眼,是否所有的 API 都是 `post`。
4. 是否出现跨服务使用相同的资源前缀。
对于大部分的公司来说,要做到 RESTful 的第一级都相当的困难。
## 数据库表适应度函数
微服务把服务间调用从函数调用变成了远程调用,这也意味着,我们并不能从 A 服务直接访问 B 服务的数据库,而是通过访问 B 服务的接口,借助它去访问数据库。但是,在某些场景下,A 和 B 是需要共用数据库(比如说,收费的 Oracle 数据库实例),但是我们需要强制性的限制 A 和 B 服务对于表的访问。
所以,我们需要分析多个服务之间是否存在对于同一个表的修改,又或者是存在对于多个表的修改。
1. 表和服务关系维护。扫描 MyBatis 等这一类的工具,生成表和服务关系维护
2. 实现『数据库表-映射服务』的快照测试。
简单来说,我们的工具在这一部分所要做的事情是:每次代码提交时,进行自动化地扫描,生成一个快照。刚其与存储的快照进行对比,判断数据库是否有问题。随后设置一个合理的调优公式,也就是这部分的架构适应度函数。
## 分层架构适应度函数
在解决了表面的问题之后,我们可以尝试达到整洁架构这一目的。对于分层架构来说,我们要做的事情可能会稍微复杂一下。不过,好在复杂的调用关系识别,已经由 Coca 实现了。
于是乎,对于我们的分层架构适应度函数,只需要做到这么一些事情:
1. 微服务之间是否存在函数调用?
2. 单个服务的所有 API 是否在同一个包内,如 controller。
3. 是否存在不合理的 common、util 模块。
4. 对于三层包架构迁移到整洁架构的改进可视化。
简单来说,就是将《[系统重构与迁移指南](https://migration.ink/)》一书中记载的部分,通过自动化的方式进行识别。
## 数据结构适应度函数
关于数据结构/数据模型,已经有一些工具可以做类似的事情。对于微服务架构来说,我们所要做的一些判断是:
- 不合理的耦合。如果一个结构体/类同时被大量的其它类调用,必然有一定的不合理之处。
- 过大的模型。值得注意的是,在一些大数据的场景下,这个反而是正确的
- 过于复杂的嵌套。
- 没有行为的模型。
然后,针对于一些不同的使用情况,还存在一些不一样的识别模式。
### 模型分析
在某些特定的场景之下,团队会将共用的模型抽取到公共的模块中,提供给多个微服务使用。这种模式本身可能是有问题的,因为在不同的限界上下文里,它些模型本身不应该是一致的。
### 相似度分析
考虑到复用和耦合之间的关系,这里不会建议它们共用的。不同服务之间需要一定的 copy/paste,但是需要考虑更好的方式,如采用类似于 `proto` 这样的 DSL 生成方式。同时,通过 DDD 的方式进行管理 —— 针对于不同的相似类型,有更好的命名方式。
## 其它细节
我们还要做好一些基础设施,比如对于模块的处理:
- 模块标志
- build.xml
- gradle
- pom.xml
- bazel
- 模块归属权
- 需求关联
- 提交信息识别(可输入式正则关系,配置化)
- 记录包-需求-服务关系
- 聚类分析
- ……
嗯,这些都不是容易的事。
## 结论
你的微服务架构适应度函数呢?Android Studio 构建 APK 分析2020-08-04T05:56:10+00:002020-08-04T05:57:51.726336+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/how-android-studio-idea-build-apk/IDEA 是我日常使用最多的开发工具之一,大概仅次于 Firefox (Google 和 StackOverflow 是生产力)和 iTerm 2。因为研究编辑器和 IDE 的关系,我研究了一段时间的 IDEA 插件的相关机制。其中作为研究的主要对象是 Android Studio 对于 Android APK 的构建部分。
最终代码见:[https://github.com/rapilab/idea-gradle-sample](https://github.com/rapilab/idea-gradle-sample)
总体的流程来说,还是比较简单的,所以太长不读版(可以直接看代码):
1. IDEA 逻辑部分:`plugin.xml` -> `BuildApkAction`
2. 构建 Gradle 模块部分:-> OutputBuildActionUtil -> OutputBuildAction -> BulidPath -> ProjectPaths
3. 准备 tasks 和 listener:assemble -> createRequest for Task Background -> createTask Listener for UI
4. 执行 tasks: GradleTasksExecutorImpl -> run -> invokeGradleTasks -> GradleExecutionHelper.execute
嗯,就是这么简单。在这里我采用的是和 Android Studio 插件类似的目录结构和路径,以方便于后期扩展和映射。
## IDEA 入口
就入口来说,Jetbrains 对于 IDEA 的配置还是蛮简洁的。只需要配置好对应的 `android-plugin.xml` 配置,然后编写对应的类实现即可:
```xml
<action class="com.android.tools.idea.gradle.actions.BuildApkAction" id="Android.BuildApk">
<add-to-group anchor="before" group-id="BuildMenu" relative-to-action="Android.GenerateSignedApk"></add-to-group>
</action>
```
接着,使用万能的 `Alt` + `Enter`,就能快速生成对应的类,并实现对应的方法。上述的配置中,还在菜单栏中添加了对应的构建命令。这样一来,对于用户来说,他们只需要点击一下,就可以执行对应类的 `actionPerformed` 方法。
随后,我们就可以从父类中获取 `project` 相关的信息了:
```java
class BuildApkAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val project = e.project!!
val projectPath = project.basePath!!
val moduleManager = ModuleManager.getInstance(project)
val modules = moduleManager.modules
val buildAction = OutputBuildActionUtil.create(modules);
val invoker = GradleBuildInvoker.getInstance(project)
invoker.assemble(projectPath, buildAction);
}
}
```
嗯,从主逻辑上来说,简化后的逻辑就是这么简单。
## 构建 Gradle 模块
就这个过程来说,稍微不太一样:
1. 注册 `FacetType`,即 AndroidGradleFacetType
2. 启动时,生成 `Facet` 配置
3. 构建时,读取对应类似的 `Facet` 配置
为了触发对应的类型构建,还需要由对应的插件来绑定对应的 type,如 Android 里的:`apply plugin: 'com.android.application'`。
随后,我们就可以调用 `OutputBuildActionUtil.create`,来创建包含生成所有模块的路径的 `BuildAction`:
```kotlin
fun create(modules: Array<module>): OutputBuildAction? {
val moduleGradlePaths = getModuleGradlePaths(modules)
return OutputBuildAction(moduleGradlePaths)
}
private fun getModuleGradlePaths(modules: Array<module>): Set<string> {
val gradlePaths = mutableSetOf<string>()
modules.mapNotNullTo(gradlePaths) {
val facet = GradleFacet.getInstance(it)
facet?.configuration?.GRADLE_PROJECT_PATH
}
return gradlePaths
}
```
如上面的代码所示:在 `getModuleGradlePaths` 方法里会遍历 `GradleFacet` 中的模块信息,并取出 `GRADLE_PROJECT_PATH`
## 准备任务和监听器
接着,我们就可以执行 `GradleBuildInvoker` 里的 `assemble` 方法,这里的代码就相对简单一些,主要是会准备一个 IDEA 相关的 `listener`:
```kotlin
private fun executeTaskRequest(request: Request, buildAction: BuildAction<*>?) {
val jvmArguments: List<string> = ArrayList()
val buildTaskListener = createBuildTaskListener(request, "Build")
request
.setJvmArguments(jvmArguments)
.setCommandLineArguments()
.setBuildAction(buildAction)
.setTaskListener(buildTaskListener);
executeTasks(request)
}
private fun executeTasks(request: Request) {
val executor: GradleTasksExecutor = GradleTasksExecutorImpl(request)
executor.queue()
}
```
即上述代码中的 `createBuildTaskListener`,它实现了一个 IDEA 的 `ExternalSystemEventDispatcher`,用于辅助系统的通知显示。如 IDEA 中的 `Build` 的 ToolWindow,用于接收、显示 Gradle 的构建日志:
```kotlin
...
override fun onTaskOutput(id: ExternalSystemTaskId, text: String, stdOut: Boolean) {
myBuildEventDispatcher.setStdOut(stdOut)
myBuildEventDispatcher.append(text)
}
...
```
于是,当我们真正执行构建任务的时候,就会在 `Build` 工具窗口中显示如下的信息:
```bash
Gradle Daemon started in 892 ms
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:compileDebugAidl NO-SOURCE
...
```
## 执行任务
随后,真正开始执行 `GradleTasksExecutorImpl`,它继承自 `Backgroundable`,顾名思义就是在后台执行。执行的步骤主要是:
1. 构建 Gradle 的 `LongRunningOperation`
2. 为 Gradle 设置 JavaHome
3. 添加构建任务
4. 执行 operation
主要的准备的执行代码如下所示:
```kotlin
val operation: LongRunningOperation; = connection.action(buildAction)
val javaHome: String = SystemProperties.getJavaHome();
operation.setJavaHome(File(javaHome))
(operation as BuildActionExecuter<*>).forTasks(*ArrayUtil.toStringArray(myRequest.getGradleTasks()))
connection.use {
operation.run()
}
```
随后,我们将调用 Gradle 及其插件中定义的 plugins 中的任务。
## 其它 - 笔记
Gradal Build Logs:
```
Executing tasks: [assemble] in project /Users/fdhuang/IntelliJIDEAProjects/MyApplication
Starting Gradle Daemon...
Gradle Daemon started in 892 ms
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:mainApkListPersistenceDebug UP-TO-DATE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:mergeDebugShaders UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:processDebugResources UP-TO-DATE
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:compileDebugSources UP-TO-DATE
> Task :app:compileDebugShaders UP-TO-DATE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:mergeDebugJavaResource UP-TO-DATE
> Task :app:dexBuilderDebug UP-TO-DATE
> Task :app:checkDebugDuplicateClasses UP-TO-DATE
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:mergeDebugNativeLibs UP-TO-DATE
> Task :app:mergeExtDexDebug UP-TO-DATE
> Task :app:mergeDexDebug UP-TO-DATE
> Task :app:stripDebugDebugSymbols UP-TO-DATE
> Task :app:packageDebug UP-TO-DATE
> Task :app:assembleDebug UP-TO-DATE
> Task :app:preReleaseBuild UP-TO-DATE
> Task :app:compileReleaseAidl NO-SOURCE
> Task :app:compileReleaseRenderscript NO-SOURCE
> Task :app:generateReleaseBuildConfig UP-TO-DATE
> Task :app:mainApkListPersistenceRelease UP-TO-DATE
> Task :app:generateReleaseResValues UP-TO-DATE
> Task :app:generateReleaseResources UP-TO-DATE
> Task :app:mergeReleaseResources UP-TO-DATE
> Task :app:createReleaseCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksRelease UP-TO-DATE
> Task :app:processReleaseManifest UP-TO-DATE
> Task :app:processReleaseResources UP-TO-DATE
> Task :app:compileReleaseKotlin UP-TO-DATE
> Task :app:javaPreCompileRelease UP-TO-DATE
> Task :app:compileReleaseJavaWithJavac UP-TO-DATE
> Task :app:compileReleaseSources UP-TO-DATE
> Task :app:prepareLintJar UP-TO-DATE
> Task :app:checkReleaseDuplicateClasses UP-TO-DATE
> Task :app:lintVitalRelease
> Task :app:dexBuilderRelease UP-TO-DATE
> Task :app:mergeReleaseShaders UP-TO-DATE
> Task :app:compileReleaseShaders UP-TO-DATE
> Task :app:generateReleaseAssets UP-TO-DATE
> Task :app:mergeReleaseAssets UP-TO-DATE
> Task :app:mergeExtDexRelease UP-TO-DATE
> Task :app:mergeDexRelease UP-TO-DATE
> Task :app:processReleaseJavaRes NO-SOURCE
> Task :app:mergeReleaseJavaResource UP-TO-DATE
> Task :app:mergeReleaseJniLibFolders UP-TO-DATE
> Task :app:mergeReleaseNativeLibs UP-TO-DATE
> Task :app:stripReleaseDebugSymbols UP-TO-DATE
> Task :app:packageRelease UP-TO-DATE
> Task :app:assembleRelease
> Task :app:assemble
BUILD SUCCESSFUL in 21s
49 actionable tasks: 1 executed, 48 up-to-date
```
Android Studio logs:
```
Executing tasks: [:app:assembleDebug] in project /Users/fdhuang/works/fework/MyApplication
Starting Gradle Daemon...
Gradle Daemon started in 868 ms
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:processDebugResources UP-TO-DATE
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:compileDebugSources UP-TO-DATE
> Task :app:mergeDebugShaders UP-TO-DATE
> Task :app:compileDebugShaders NO-SOURCE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:dexBuilderDebug UP-TO-DATE
> Task :app:mergeDebugJavaResource UP-TO-DATE
> Task :app:checkDebugDuplicateClasses UP-TO-DATE
> Task :app:mergeExtDexDebug UP-TO-DATE
> Task :app:mergeDexDebug UP-TO-DATE
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:mergeDebugNativeLibs UP-TO-DATE
> Task :app:stripDebugDebugSymbols NO-SOURCE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:packageDebug UP-TO-DATE
> Task :app:assembleDebug UP-TO-DATE
BUILD SUCCESSFUL in 10s
21 actionable tasks: 21 up-to-date
Build Analyzer results available
```</string></string></string></module></module>代码评论家2020-07-27T12:40:19+00:002020-07-27T11:40:46.815356+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/code-commentator/日常的搬砖过程中,我们总会因为代码上的一些设计问题,进行争论。而最后结果呢,可能就是『show me you code』,又或者是『你行你来』。
在这里,我们要抛开一个因素来讨论问题:**规范**。团队在成长的过程中,必然要养成自己的规范。本身缺少规范而讨论规范,这就不是个问题了。如果规范不对,这又是另外一个问题了
所以,我们的重点还在于:**设计**,本身就存在一些主观因素,没有绝对的好与坏。人们还可能:
- 针对于未来而设计,假想一系列的设计 —— 虽然这是错的,但是要证明这是错的很难
- 因为自己的喜好,试图去说服别人你也应该这样做
- 因为不平行的双方地位、角色,需要花费额外的功夫
而抛开设计的角度,也总有一些人喜欢去对别人的代码『品头论足』。
**评论别人的代码,并没有任何问题** —— 代码是写给人看的。就好像是人的衣着打扮一样,有人擅长打扮,就会有人喜欢看。当然了,口味也并非是一成不变的事物。以前,你喜欢成熟风格,以后你可能就喜欢少女风。
## 评论者分类
从我们的习惯来看,我们会对这些评论者进行一些分类:
- **符号党**。“我代码写得不多,你也不要骗我,你这里少了一个分号;Go、JavaScript、Kotlin 就应该写分号”。
- **键盘侠**。“怎么做出好的设计,我不知道,但是你是错的。”
- **评论家**。“你这写错了,你应该这么写,blabla”,结果:评论家也写不出来。
- **王者型**。“你这写错了,我给你看我写的一个示例。”
简单来说,就是能不能设计 + 会不会写:
- 什么也不会:符号党
- 不会设计,不会写:键盘侠
- 会设计,不会写:评论家
- 会设计,也会写:王者型
我并没有想抨击哪一类人的意思,因为从不会到会,就是一个成长的过程,是大部分人要经历的一个过程。
我们非常讨厌键盘侠,因为他/她什么也不会,就会瞎 BB。那么,剩下一个问题是,评论家到底是好还是坏?
## 评论家的自我修养
我们可以将评论家这个角色的能力,将其他/她评论家角色作为一个对比。就会发现,作为一个合格的代码评论家,她/她应该懂得:
- 什么是好的设计
- 阅读过大量的好代码
- 阅读过大量的糟糕设计
- 知道如何在合适的时候,使用适合的模式
- ……
并且,他/她应该持续不断地:
- 阅读大量的代码,提升自己的眼光和眼力
- 学习不同的语言,了解他们不同的使用模式
- 对设计、发展趋势,持续不断地深入
- 偶尔能写写 demo
但是,你要知道要成为一个合格的评论家很难,因为大多数人:
1. **知道各种设计模式,但是难以在正确的地方用它们。**
2. **知道什么是好的设计,但是都用在错误的地方。** 往往会做过度的设计
3. **很少花时间阅读别人的代码,因为别人写的代码都是 “屎”**。
4. **没有阅读过好的代码**。
5. ……
好在,只要多多努力就能成为代码评论家。
## 如何成为代码评论家?
……
## 结论
没有代码能力,成为不了评论家。README 驱动开发2020-06-16T12:05:09+00:002020-06-16T05:05:48.458747+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/readme-drive-development/最近,我又挖了几个开源项目的坑,Ledge、Ledge Framwork、Igso 等等。每次挖新坑的时候,经常性地都要花很多的时间,想着怎么编写 README、完善 README。而就是这么一个简单的 README 的编写,它都要花费我相当长的时间,或是几个小时,或是几天。
问题来了,我真的是在写 README 吗?
## 引子 1:不止自述的 README
> README,又称“自述文件”,是随着软件发布的一种说明文件,里面通常包含有软件的描述或使用的注意事项。
在众多的开源项目里,README 是我们最常修改的文件之一。每当我们添加了一个新的功能,一个新的特性的时候,我们的第一反应就是更新一下 README,以向世人宣告我们的开源产品又添加了新的功能。
所以,在我的那本《GitHub 漫游指南》里,其中有一部分的话题就是关于 README 的编写。对于一个 README 来说,有这么几个关键要素:
- 一句话简介。这个项目做什么?
- 项目介绍。它解决了什么问题
- 特性。包含已完成和待办
- 使用指南。如何一步步使用这个项目
- 示例。hello, world 示例
- 开源协议。
对于某些项目来说,它们还有:
- 项目对比。如性能、特性等
- ……
其中的一个主要原因是,在开源领域 GitHub 有比较高的话语权。而 GitHub 使用了 README 作为项目的『首页』,作为了整个开源项目门房。而正是这个首页,让我们重新意识到 README 的重要性,刷新了 README 的作用。
## 引子 2:产品而非项目
紧接着,在上一篇文章里,我强调了开源产品而非项目。再重新以产品化的维度来考虑 README 的几个要素,我们就有了者的对应关系
- 电梯演讲 <-> 一句话简介
- 用户旅程 <-> 项目介绍
- 竞品分析 <-> 项目对比
- 用户故事 <-> 待办清单
- 示例和使用指南 <-> 用户手册
- ……
换句话来说,通过编写这个 README 的同时,相当于是一次关于这个开源项目的产品化思考。
## README 驱动开发
由于,看不到一个成熟的 RDD 定义,所以我先按我的理解定义出第一个版本的 README 驱动开发:
> README 驱动开发是一种通过事先编写 README 的方式,以一步步驱动出受用户欢迎产品的软件开发方法论。它被广泛的应用在开源软件领域,旨在通过从使用者的视角来思考软件的意义和用户体验等。
从这个角度来看的话,在编写 README 的时候,我们要让使用者能:
- 一眼能理解项目的目的。
- 快速了解项目的效果。即通过截图或者是线上 DEMO 等方式
- 迅速地获得反馈。即我只需要执行一个命令,或者是打开网页,就能知道的效果。
- 知晓他她们的权利与义务。即 LICENSE
- ……
也就是为什么 README First 在开源领域非常流行的原因。
### README First
README First 即通过先编写 README 的方式来:
- 重新思考项目的价值,而无需真正动手写代码。
- 吸引更多的潜在用户或者是开发者。
- 做正确的事情。
- 构建更好的开发者体验。
它远远要比你做完一个开源项目,再去编写 README 来得快速、可靠。特别是,对于 GitHub 这样的开源平台来说,当别人为你的项目 star 的时候,他/她的 followers 就可以看到这个动态,进一步地提升项目的传播效果,进一步地为你带来更多的 star。
而如果你的项目的 README 不够吸引人的时候,那么你就失去了这种先发优势。
### 持续更新
它是一份初期的文档,随着项目的进行,越来越多的需求将会由社区反馈过来,文档也会越来越完善。
### README 测试
顺便一提,最近我开始在寻找一种新的测试方式,README 测试。
既然一个 README 可以写成结构化的,那么它必然也是可以测试的。它可以是类似于 Gauge、Cucumber 等这样的 BDD 框架,也可以是解析 markdown 后生成的特定的测试用例。
## 结论
程序员恨别人不写文档,自己恨写文档。
README 就是一个轻量级的文档方案。逆数字化:数字化时代的自由在何处?2020-06-01T12:03:36+00:002020-06-01T12:04:00.456996+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/thinking-in-anti-digital/很早以前,我便想着写一篇文章吐槽一下数字化时代。如果你熟知我在开源世界的贡献(代码 + 内容),就知道我一直是开源软件、自由软件的拥趸:RMS 一直是对的 [🐶🐶🐶] 。
首先,在这里讨论的是法律规定以内的自由,也就是守法前提下的自由。
直到,最近发生了一些事情,我才开始重新重视了一下这个问题。从现象来说,大致是这样的:
**数字的『所有权』**。我买了一本电子书,但是我只有阅读版权,而非所有权。如果是一本实体书,我可以把它出借个我的朋友,又或者是再次出售。除此,内容的提供商,它们『随时』有可能剥夺了我的使用权。尤其是,在中国特色的内容体系下。
**随时可消失的数字产品**。这个故事的起源是,我开通了网易云音乐的 VIP,而我在下载到本地的音乐会被它们删除。因为它们失去了版权,所以我们的相关歌曲都会被删除。当然了 QQ 音乐也是如此。相信的一个问题是,它们可以随意删除你的内容、隐藏,如之前的阿里一哥事件。
**数字服务商的底限**。记得我在去年的文章里说到:因为隐私问题,我从一个 Android 手机用户,变成了主力 iPhone 的用户。其中的主要原因就是隐私问题,因为国内大小公司的底限越来越低。它们不仅可以拿你的隐私给你卖广告,还可以删除你手机上的内容,参见:『如何看待 QQ 疑似侵犯用户隐私,自动删除阅文作家的手机图片』。
**消失的内容**。除了,法律规定的内容之后,我们还会遇到其它情况的内容消失。如你可以在微博上黑腾讯,可以在微信上黑阿里,但是不能反过来。因为它们是 ”母体“。
**无处不在的广告**。
数字化时代之下,我们并非活在一个越来越好的世界。
曾经,我一直担心的主要问题是输入法对于舆论的监控,现在我反而更操心大部分人能不能从数字化奴隶里走出来。
算法已然操纵了一部分人,另外一部分人也不远了。
----
你觉得呢?2019 节点: Love Wife & Change Life2019-12-31T11:37:54+00:002019-12-30T12:34:23.915843+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/annual-2019-love-wife-change-life/> 为什么你还在 ThoughtWorks?
因为不加班。人生总会有很多的选择,在决策的那一刻,你不知道对与错。但是,开心就好。
12 月初,ThoughtWorks 开始了 Annual Review 的 Kick Off,我开始总结这一年的工作,与此同时,我也开始总结我的 2019 节点。今年仍然是『平淡无奇』也过完了重要的一年。
太长不读版:
- 爱情上,领证了,和 @ 花仲马一起来到了杭州;还差好多钱买房,还得考虑办婚礼的事情。
- 职业上,从深圳 office 转到了华东 MU,从华东 MU 转到了咨询团队,开始了在 TW 的出差生涯,还有加班生涯。
- 设计上,每天画了一张画,一年 365 张画;插画,作为文章的一部分,已无处不在。
- 写作上,出版了《前端架构:从入门到微前端》,印刷量在 7500 左右,有希望在一年内达到 1 万。
- 编程上,写了更多的工具,愈加丰富的重构经验,顺带深入软件体系的架构。
- 斜杠上,尝试电子产品的评测,写作相关的收入差不多是前两年之合。
- 影响力上,开始了 International 的尝试 —— 时间仍然是一个限制因素。
- 社交上,我退出了大量的微信群,专注于生产内容。
嗯,还有游戏,文明大法好。
所以,对比一下上一年的目标:
1. **技术隐私**,打造了自己的 Serverless 密码管理器:https://github.com/phodal/mopass ,作为一个 Chrome 插件,它很好地作为了我的二次管理认证工具。
2. 非技术写作。好似没有一个开始,似乎也不是一个好的目标。借这个名义,我看完了《刺客信条》的小说。
3. 工具。开发了更多、更有意思的工具,还有更多的 PoC。
4. 设计。天天练习插画,更快的画画速度,质量上也有所提升。也烧了更多的钱绘画工具上。
5. Coach。幸好在上一年里,它不是一个目标,扶不起的人太多了。对的人,对的事,才能成——借口。
6. 影响力。受众级别比上一年有所提升,还有更深度的内容。
不算太好,也不算太糟糕。
## 编程:平台 + 工具 + DSL
惯例,依旧是工作 + 业余。
### 工作:Platform & Tools
工作上没有圈,也没有点,今年的工作简直是一团糟,还加了人生的第一次班,而明年还会有更多。Work–life balance 不断被打破,就得寻找一个更合适的地方 —— 如果有的话。
#### 平台
上半年,工作的主要内容是大前端开发框架 / 平台,所以研究了一段时间低代码编程,写了那篇《无代码编程》的文章。一番操作下来,发觉重点在于 AST 和 DSL。因此,除了开发一些日常的工具之外,我开始撸 dilay 框架,创建了 subal 项目……。作为一个苦逼的 Tech Lead,除了项目相关的两个团队,还要照顾公司的其它多个团队。日常不是一般的忙,开会、开会、开会,还得做架构??还要评绩效??填别人的坑??还有写代码……
一个也不能落下,每个都落下了。
做了一个大前端开发平台,这样一折腾下来,收获倒也是挺大的,我对研发体系有了更深入的研究。考虑问题的时候,比以往更加系统,更加全面。文档、脚手架、示例应用、CLI 工具、IDE / 编辑器集成、售后 Q & A 等等一个都不能少。于是,在项目上写了对应的 CLI 工具,尝试把文档融入了开发工具中……
没毛病,老子可以各种吹了:不要做平台。我 Phodal 就是……,我也不会……。
#### 工具
下半年,beach 了两三个月,写了个重量级应用 Inception,然后,转到了咨询团队。来到了新的 U,有了更多的灵感和时间去写工具,也从公司大佬新哥那获得一堆 Todo List。所以,下半年在业余时间写了更多的代码,写了更多的 DSL。所以,DSL 成为我这一年的一个主要风向。
我有了遗留系统重构工具: [Coca](https://github.com/phodal/coca),还有了 Badsmell 识别工具:[Sprite](https://github.com/phodal/sprite),以及对应的重构建议工具:[fanta](https://github.com/phodal/fanta) ……。
它们都是使用 Go + Antlr 写的,target 是便宜的后端开发语言 Java。一顿瞎操作下来,除了更懂 Java 语法,我还学会了 Go。明年,我就可以 Rust + WebAssembly 搞 C or TypeScript 的语法分析了,一下子学会好几种东西的感觉好爽。
卧槽,又要兴奋的失眠了。想想,还是很美好的。
### 业余:工具 + DSL
2018 年底,我的 GitHub 数逼近 40,000;2019 年底,也有 48,615了,可不敢说逼近了。明年我的目标就是 50,000 star 的时候,发个朋友圈,哦,不对应该是 Twitter。
依旧的 Serverless 仍然是我的后端最佳选择,我用它写了我的密码管理工具:[moPass] [https://github.com/phodal/mopass](https://github.com/phodal/mopass) 。继 ADR 和 Phodit 之后,我的另外一个日常使用工具。我的业余项目上还上手了 Golang,嗯,真香。
#### Architecture
今年,有幸可以在项目中引入对于前端架构的探索,进一步地完善了我的前端架构体系,也产生了前端架构守护框架 [Dilay](https://github.com/phodal/dilay),完美的造了个 PPT。
所以,在实践了 Domain Driven Design 和 Clean Architecture 之后,我开始思考 One Architecture 的可能性,尽管我已经用 JavaScript / TypeScript 证明了它的可能性:[https://github.com/phodal/one](https://github.com/phodal/one)
然而,Java 仍然是后端的主流语言,一个 Java 转 JavaScript 的工具不可缺少,而编程语言有那么多,所以我们需要的一个是 DSL 转任何语言的工具。也就是我最近在做的 Code 项目: [https://github.com/phodal/code](https://github.com/phodal/code) ,实践上还有待完善,只是 hello, world 出来。大抵,还需要半年地时间完善。
#### 基准化
考虑到人总是会老的,Phodal 是人,所以 Phodal 会老的。我继续写工具、文章来沉淀知识,以用于以后甩出一个链接(装 x 神器):
- [Clean Architecture]:[https://github.com/phodal/clean-frontend](https://github.com/phodal/clean-frontend)
- [React Boilerplate]:[https://github.com/phodal/react-boilerplate](https://github.com/phodal/react-boilerplate)
- [New Project Checklist]:[https://github.com/phodal/new-project-checklist](https://github.com/phodal/new-project-checklist)
- Inception 工具:[https://github.com/phodal/inception](https://github.com/phodal/inception)
- [Path to Productioon]:[https://github.com/phodal/path](https://github.com/phodal/path)
- [Tech Lead Assessment]:[https://github.com/phodal/tla](https://github.com/phodal/tla)
对应的还有一篇相关的文章:《[如何创建你的应用脚手架](https://www.phodal.com/blog/how-to-create-application-boilerplate/)》,年轻就是好,对了,还有 Tech Lead 的基准化:《[Tech Lead 的养成](https://www.phodal.com/blog/how-to-be-a-tech-lead/)》。
### Everything as Code:DSL
作为一个 Markdown 资深用户,除了进一步完善我的 Phodit,我还结合 Markdown 写了很多工具:
- Markdown 定制文档工具,见 《[【架构拾集】基于 Markdown 文档展示系统设计](https://www.phodal.com/blog/architecture-in-realworld-markdown-based-document-system-design/)》
- Markdown 转思维导图,见 Inception 工具。
- Markdown 转 PPT 工具 [mdppt](https://github.com/phodal/mdppt)
不过呢,定制别人的 DSL 始终是比较一个比较 hack 的方式,所以如何卓有成效地开发一个 DSL,便成为了一件非常有意思的事。所以,公司大佬说的 DSL as Data, Data as DSL 仍然是一个不错的目标。
在那篇《[云开发:未来的软件开发方式](https://github.com/phodal/cloud-dev)》中,我提到了在未来几年,我要做的一些事情:
- 更易于实践的微架构
- 完善的代码化体系
- 寻求合适的协作设计
所以,设计和抽象 DSL(Domain Specific Language)将成为了我未来几年一个重要战略。也因此,从大体上来说,它仍然是我的下一年目标和计划。
## 写作:100 万浏览量 + International
年初,出现了一个新的里程碑,我的博客 phodal.com 累计访问量突破了 1,000,000 万。
考虑到在微前端和 Clean Architecture 的实践,已经和国外的速度差不多,外加国内的 996 环境。所以在在今年年中,我尝试将 International 作为 Impact 的一个新方向。因此,在这篇总结里,我把写作相关的部分分为了大中华区和 International 区。
### “大中华区”
虽然我在写新书的时候,看了很多小说,试图去改进,但是依旧在豆瓣上被吐槽『写出来太理论太像翻译腔』。没救了,没救了,写过 776 篇博客的我,表达能力依旧还有巨大地提升空间。
#### 出版
今年 5 月出版的《[前端架构:从入门到微前端]》,出版社的总印刷数已经有 7500(并非卖完),豆瓣读书上的评分也有 7.6 分 —— 比前两本书多出了一份。瞬间又有动力准备下一本书了,只是怕是没有那么多时间了。
颇为遗憾的是,出于字数少的原因,我在『前端架构』 一书中多加了一个章节。而由于出版时间太早,少了后来实践的『Clean Architecture』——这是另外一个前端所需要的分层架构模式。将它与 Serverless 配合,就形成了我们所需要的 One Architecture。
#### 文章:体系规划
从内容上来看,我对今年的文章倒是颇为满意的:
- 《无代码编程》
- 《整洁架构》
- 《构建可信的软件架构 10 要素》
- 《微前端架构》
- 《管理依赖的 11 个策略》
- 《云开发:未来的软件开发方式》
- ……
但是如你所料,我创建了一篇又一篇地长文章,手就有点疼,坐久蛋也疼。
只是呢,好像也没有新的亮点了。
### International
今年从我的观察来看,我在开源领域开始逐步走向非中文世界。Mooa 和 ADR ,迎来了一个又一个的国际友人的支持。我的 GitHub followers 也多了一个又一个的国际友人。
内容国际化,是今年年中开始的一个新的方向 —— 之前的另外一个国际化目标是:**开源软件的国际化**。
虽然我的英语语法并不是那么靠谱,但是 Google Translate 也差。我相信同翻译腔一样,只要会被人吐槽英语语法不行,说明我已经成功引起大家地注意了。
#### English Articles
由于种种原因(诸如文章太长懒得翻译、高质量的文章不够多),产出仍然相对比较少。
不过,也算还行,我在 Dev.to 上创建了我的账号,发了一篇微前端相关的文章,还有一篇 Clean Architecture 相关的文章,也产生了一定的影响力。除了几十个的掌声,一万左右的单篇文章浏览量,还有 StackOverflow 有相关的问题指向了我的文章,笑而不语~。
所以,继续翻译更多的文章吧,是时候依赖反转一下了。
#### Review
过去的几年里,Review 英文书籍显然是国际赛道的一部分,只是呢,当时呢,这个 business line 还没想好,现在也没想好。
今年还是 Review 了 Packt 出版社的一本书籍《Web Development with Angular and Bootstrap - Third Edition》,遗憾的是近一二年 review 的书,都没有被引入国内。
不管怎样,国际化应当成为 2020 的一个继续前进的方向。
## 设计
我换了一个又一个的工具:
- iPad + Apple Pencil。买前生产力,买后爱奇艺
- Wacom Intuos Pro。专业级手绘板,相当的不错。
- 绘王 Kamvas Pro 16。嗯,解控屏,效率就是高。对于我这种非专业级选手,还是非常好用的。
- Wacom Intuos Mini。出差专用,个小板子虽然不那么好用,但是我也算是习惯了。
终于,我还是没画好画。
年中的时候,我尝试录制绘画的过程到 B 站、抖音上。但是,画的时候往往发现,录视频的时候,会影响我画画,也就作罢了。
### 画:365 天
上一年考虑到设计的边缘化,我开始采用日常练习的方式,来提升这方面的感觉。
[Daily](https://github.com/phodal/daily)
最初设计的目标是每天 0.5 小时,但是受编程状态的影响,往往会被挤压到 15分钟。好在,随着练习的进一步 ~~一深~~ 深入,我还是能在短暂的时间内,画出一些不错的作品。
稍有不同的是,受出差的影响,我有时候不得不在早上画画。
Whatever,我已经有一堆画了。
所以,如果没有问题的话,它仍将作为我下一年的日常。问题的关键在于:如何结合文章的意图,创建对应的作品?
### Design Thinking
寻找更广泛维度的设计思考。
TBD。
受限于有大量的代码要写,以及好像没有遇到好的设计师。没有灵感嘛,就先这样,慢慢来,路子还长着。万一明年可以遇到可以愉快合作的小伙伴呢。
## 其它
人生苦短,一点点做
## Hello, 2020
嗯,我要登机回杭州了。
咦,2020 呢,要小康。
哦,不对,说好的婚礼呢。写代码的核心2019-12-16T12:54:51+00:002019-12-16T04:55:25.439546+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/coding-skills-level/最近,经历了一系列代码吐槽事件之后,结合公司大佬的观点之后,大体上对于程序员的编码 level 有一个更好的认识。所以,我决定写一篇文章,以此来划分不同的程序员。我知道为别人打标签是不对的,政治上是不正确的,但是我不会明着对你说的。每个人在自己心里都有的一杆秤。
注意:这篇文章所针对的并不是程序员相关的编程技能,而只是单纯指码代码本身。算法好的人,不一定写出好代码;架构好的人,不一定能写出可读的代码……。
## 职业规划
我都是从新手程序员过来的,所以,倒也不是想说明什么。只是呢,如果还是精进的话,我们还可以一起多加练习。也借此,输出一份我所知道的程序员的分界线。它也不是一份“标准”,只是我所以为的以为。
倘若打击到你,也不必忧伤。除了成为专业 的程序员之外,你还有其它的诸多选择,成为一个技术管理者、项目经理等,成为一个多面手。又或者是去送送外卖、滴滴司机,过着普通的生活,也不错。生活为你打开一扇门时,也会为你关上一扇门。
况且,到了这某一把年纪,你也还不好写代码,也挺不容易的。也是时候重新做做职业规则了。
如果你还是想和我一样继续写代码,那么这篇文章的剩下部分,倒是颇为适合你的。
## Level
起先,我是打算写一个相关的维度,后来想了想,这样一来有些复杂,就算了。于是乎,我就写一个简单的 Level 表吧。
### level.-1 可工作的代码
书单:《30 天入门 xxx》
> 任何一个傻瓜都可以写出计算机可以理解的代码,唯有写出人类容易理解的代码,才是优秀的程序员。 ——Martin Fowler 《重构》
- 懂得一门编程语言。
- 能写出可工作的代码。
性能好不好不重要,人能不能读不重要,可以赚钱就行了。
### level.0 会用工具
> 使用工具是智力的标志,会用工具是聪明的标志。
书单:《鸟哥的 Linux 私房菜》、《提问的智慧》
从物种进行的角度来说,会使用工具是一种智力标志。而选择和使用合适的工具,则是智慧的表现——前提是你有合适的工具。
- 类 Unix 系统。毕竟这年头,是个程序员都得会点 GNU/Linux。
- 使用搜索引擎的技巧。广泛的搜索引擎,既要会使用 Google、StackOverflow,还会使用 GitHub issues。
- 可以用命令行。
- 懂得如何提问。
但是,你不会使用高效的工具,也不会意味着,你就不会高效地写代码。
### level.1 能写代码
> 一个 60 分的程序员,应该能**按时**写出能符合需求的代码。
这里的按时的是指在合理估算的情况下,如果本身估算不合理的话,那怎么都不能按时。
书单:《七周七语言》(名字好而已)
- 能考虑错误处理
- 进行防御性编程
- 懂得**多门**语言,能快速上手。
- **合适**的锤子(🔨)。工具箱里不只有一个工具。
也总有人不进行错误处理,不考虑正常的编程的异常条件——极端条件除外,毕竟大部分的开发人员都不是专业的测试。也只有经历过一些 bug 的风风雨雨,才会尽可能地、考虑尽可能多地极端条件。
### level.2 可以代码
可以代码,即能放心地把活交给一个人,在代码质量上又不会出现问题。
书单:《Head First设计模式》、《编写可读代码的艺术》、《代码整洁之道》、《重构: 改善既有代码的设计》
- 会写单元测试。
- 懂得设计模式。
- 良好的代码组织方式。是的,从某种意义上体现了架构的能力。
- 使用工具替换重复性工作,比如正则。
- 合理的估算编码时间。这一条件本来不是在里面的,可是呢,真的有好多人不会估算。
我觉得吧,大部分程序员都在 Level 1 和 level 2 间徘徊。主要是韭菜一批又一批的,代码写得好的就升官发财了,写得不好的还在继续写。能一心写好代码的人,真心不多。
关于设计模式,我并不是设计模式的偏执狂,只是呢,模式是一种通用语言,用来快速来与别人交流。毕竟,模式与原则是专门给智商不够的人类设计的。
### level.3 会写代码
作为一条新的分界线,这里的你应该是懂得什么是好的代码,还能在日常的工作中写出好的代码。与此同时,你具备把不算太烂的代码,重构到易于阅读的代码。
书单:《测试驱动开发》、《重构与模式》
- 会写各种类型的测试
- 掌握高级重构技能
- 合理的使用设计模式
- 能重构代码到合理的设计模式
在这个项目里,我们在定义什叫会写代码,为了不打击到大家。我还是把这个层级放低一点,所以当我们说会写代码的时候,说的是:能写好代码。
### level.4 懂得代码
书单:《领域特定语言》、《Ruby 元编程》、《编程语言实现模式》、《编译原理》、《计算机程序的构造和解释》
- 制造工具,替换自己
- 设计抽象 DSL,完成重复性的工作
- 写一个编程语言,能用在日常的开发中
- ……
嗯,大概就是这样。
### level.999 写 PPT
我本来想说架构是其中的一部分,可是后来发现吧,有些事情,它能水到渠成。
可惜,我还不会。
## 结论
对了,你还具备好的手速,才能实现好的代码。客户现场,三年2019-09-09T12:22:44+00:002019-09-09T03:23:27.619790+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/client-side-three-years-summary/再三犹豫,终于在 rebase 到上海之后,还是决定写下这篇文章。三年多以前,我 rebase 到了深圳,开始了国内交付项目的旅途,我也几乎成为了待在该 account 下最长的 **developer** 没有之一。在那之前,我在 offshore 项目,混吃混喝混 session。
从 offshore 项目下来时,我不太适应客户现场——整个项目的人驻场开发,并需要在一定程度上遵循客户的规范。所以:
- 没有每天的水果
- 没有每天的运动
- 没有很多 session(PS:相比于西安、成都 office)
- 没有 people 关心(PS:还是有的)
- 没有 workshop 和各种培训
要知道在 office,那可是早上 10 点有水果,下午 2:30 有水果,5:00 后有各种 session,还管饭。渐渐地,我远离了 office 的八卦,贴近了客户的八卦。(PS:所以,最后我 rebase 了,笑~。)
## 成长
对于每个刚进入一个新的公司,一个新的领域的人来说,成长是一个永恒的话题。而时代在变化,哪怕是退休的人呐,也都在学习怎么使用微信抢红包。
### 技艺
从技术的成长来说,只要有同等的学习时间,那么成长都是差不多的。所以,从某种程度上来说,我觉得不管是哪种类型的项目,靠的还是自己。不过,稍有区别的是,在 office 的项目,有更多的时间,也有机会接触到更多的机会:
- 接触到新项目。这一点对于不擅长表达的同学来说,在客户现场就少去了很多机会。
- 培训机会。office 也不大,一有风吹草动大家都知道了。
- session 和 workshop。
从技术挑战上来说,国内项目周期相对较短,比较有机会接触到有技术挑战的项目;当然了,国内项目的交付压力和业务挑战,往往会比 offshore 项目要大。从生命周期来看,长期项目更有利于初级技术人员的成长——可以学习到先进的开发思想,培训出好的开发习惯。然而,国内交付项目的周期都比较短。值得注意的是,这里是从总体上来看的或者说叫平均值。因为一个优秀的 Tech Lead,会帮助大家更好的成长。
因此,这么一来看,国内项目比较适合中级开发人员的成长——需要不断平衡技术、业务、实践优先级。什么时候处理技术债?什么时候放弃一些技术实践?什么时候优先关注于业务开发。每个问题都很难,你总会遇到各种挑战。
### 应变能力
我第一次来到客户现场的时候,并没有任何相关培训,也缺少一些相关的经验。大抵是因为在当时,国内交付项目还不成规模。这就可能造成诸多的事故,反正我是见了不少,笑~。
事实上,第一个在客户现场的人都是**半个真正的咨询师**——尽管名义的 title 是咨询师,但是干的是普通开发的话。但是,从应对的挑战和职责来看,每一个人都是咨询师。所以,并非所有的入场员工,都会拥有相关的角色认知。
作为一个咨询师,你只需要比客户快半步即可。但是作为一个在客户现场的开发来说,你得比客户快一步。你每天都和客户一起工作,技术社区一有什么风吹草动,客户就知道了,诸如于 NPM 事故,React Native License 问题等等。外加一些无良的自媒体的捕风捉影,它们的错误言论,总会让客户对你的解释将信将疑。遇到的这样的每一个问题,都需要快速地做出响应。
如果比客户晚半步,那么还可以:『回去研究研究』;『建张卡做』。
### 沟通
于我而言,在这段期间里,与技术的提升相比,在沟通上的提升反而更大——毕竟从 10 分到 60 分,远到 60 分到 80 容易得多。
> **个体和互动 高于 流程和工具**。 『敏捷宣言』
当你直接和客户一起工作的时候,快速的响应比什么都重要,而沟通是每时每刻都在做的事情——当然了,你得注意沟通的顺序:**内部沟通,随后再与客户沟通**。尤其是在遇到问题的时候,更考验相关的应变能力。记得先在内部把问题抛出来,再在内部去讨论怎么解决——或者与客户一起讨论,如果已经取得客户的信任。
而与 offshore 项目不同的是,每一个在客户现场的人都**有机会**参与到:一个需求从想法到上线的完整过程。这到是也蛮能提升开发者的各种能力,沟通、表达、快速响应。毕竟有的时候你要去**截胡**——将不可能实现的功能,扼杀在摇篮中。这样一来就不会出现那种奇怪的需求:根据手机壳的颜色来适配主题。
总之:**实时沟通,注意言行**。
### 赋能 ta 人
作为一个 ThoughtWorks 的 developer,赋能 ta 人是你所需要具备的能力。你会经常分享一些相关的 session,又或者是手把手的结对编程,又或者是在 code review 的时候多提一些意见,还有针对于新人布置一些作业等等。
就当前而言,ThoughtWorks 引以为傲的是**工程实践**与**抽象复用**——与国内的 ~~B~~AT T 相比,还是领先的。对于某一特别的领域和技术,如果它不能快速的复制应用到整个组织,那么它就缺乏竞争力。而哪怕能快速复制,还会遇到对方水平的问题。可归根到底,这些都依赖于有能力的个人,哈哈哈。
**赋能客户**。你会遇到的挑战,并非是把东西教给某个人,而是某些人不愿意学习,又或者是持相反意见。这就是你要面临的挑战,但是不妨着以让对方去尝试的角度来说服 ta 试用,万一真香呢?
**赋能 TWer**。在客户现场的时候,你也得去保护那些新的团队成员,或者是第一次来到客户现场工作的人。而取决于团队的规模,你可能无法保护所有人,只可能尽可能去分享其中的经验。而当你和新的 ThoughtWorker 一起工作的时候,你,你经常就会遇到一些挑战,诸如于:如何在赋能的同时,体现这个新 TWer 的挑战。尤其是对于 Tech Lead 来说,这种挑战更为严峻。所以,『这个卡交给 xxx,一天就能做完了』。
当你赋能到累、没有成长的时候,可以试着把这个工作分享出去。
## 四步曲
在之前的那篇《项目初期的最优技术实践》里,我抽象了现场开发的三个时间:
- **技术准备期**。进行一系列充分、不充分的技术工作,从搭建脚手架,到部署测试等等。
- **业务回补期**。填补第一个时期造成的业务落后问题,技术实践业务来证明技术的价值。
- **成长优化期**。持续地对项目的技术和业务进行优化,以实现开发及业务人员的诉求。
而对于客户现场来说,对应的则是四个步骤。:
1. 糅合团队,应对挑战
2. 保证交付进度
3. 赋能客户
4. 扩大影响力
嗯,大概就是这么简单。
### 1. 糅合团队,应对挑战
在第一阶段,你可能随时可能被 challege,原因有多种多样的:
- 设计细节有些小问题
- 和原有系统出现差异
- 架构设计得很好,但是其它开发人员不一定能接住
尤其是,我们在项目初期的建立开发规范的时候,会遇到一系列的挑战。诸如于和客户原来的开发习惯不一致——多数时候,可能是一些不好的、落后的习惯,那么客户……。
不管怎样,**妥协是最后的可行办法**。不过,作为一个以咨询师 title 存在的开发,你所能做的事件是:**说服客户采用**。
### 2. 保证交付进度
出现多种原因,会出现迭代 delay 的情况,如需求变化,预期的对应出现问题。其中的原因与我在《项目初期的最优技术实践》讲述的差不多。
不过,还要记得偿还你的**技术债**(务)。
### 3. 赋能客户
如上所述。
哪怕是客户有其它的 vendor,他/她们也可以成为你的朋友,帮助项目更好。
### 4. 扩大影响力
手疼,写不下去了。
总之,作为一个乙方要从长远来考虑问题,从 A 小组,到大组,再到整个部门。
#### 交接(可选)
不同的客户有不同的利益,
#### 演进(可选)
嗯,为未来做准备。
## 其它
### 保持你的幽默
当你是项目的 Tech Lead / Team Lead,适当地幽默气息,可以在困难的时候,看上去不会这么艰难。
### ……
TBC编程的休渔期2019-08-31T08:08:34+00:002019-08-31T08:09:28.225564+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/rest-in-coding/在科技公司,诸如于 ThoughtWorks,当你在等待项目 / 两个项目之间 / 没有项目的时候,这段时间称为 bench (冷板凳,笑) 又或者是称为 beach。
Beach 的时候,你并非无所事事,有人会找你打黑工,有 HR 会找你看作业,你也会有想做的很多工作。这大抵是在 ThoughtWorks 做搬砖工人另外一个优点,你有时间休息。还有另外那个优点是,你可以很容易地换不同的颜色的砖搬搬。
在这种模式之下,人本身像敏捷的 sprint 一样,有 IPM,有 Retro,休息完了,再重整旗鼓。
## 真正意义上的休息
我们在 ThoughtWorks 的休渔期,那就真的是休息了——因为你已经离开了上一个项目,你大可以像我一样在这一个月里休上 10 天左右的假——如果你有 20 天年假的话。在这一做时间里,你真的是在休息,不用烦恼于项目——根本没有项目。
去年我和 @花仲马 在马来西亚玩的时候,正好是在项目上的时候,偶尔还得操心一下项目上的事情。
而我当成为一个前端开发人员,便成为了一个非常普通的开发人员,有了更多的时间——即是好事,又是坏事。不过,对于这篇文章来说,它就是好事。
## 爱好:做爱做的事
爱好成为了工作,并不是一种容易的事。特别是当你疲倦于业务逻辑的时候,想在技术上寻求一些突破的时候,你需要时间、时间,还有 TM 的时间。所以,你总会去 balance,到底是钱还是爱好?开心最重要,如果钱能让你更开心,那么肯定是钱重要。当你拿不到更多钱的时候,便会去找一些个人追求,寻找更高的目标。
编程,并不是你唯一的爱好。beach 的时候,适合于拾起你喜欢做的事情,比如 Steam 上的游戏。
又或者是其它你喜欢做的事情。我已经在不断将爱好(如绘画)融入了日常的生活,所以平淡的生活,就已经足够了。
## 寻找灵感
每次 beach 的时候,我总会带入上一个项目的诸多灵感到下一个项目。真实世界的编程有太多的无奈,你知道有一些技术,又或者是你能发明一些技术,它们可以改进系统的架构,让系统变得更好。可出于种种的原因,或是出于进度压力,或是出于系统稳定性的缘故,你根本没有多少地机会在现有的项目中实施,于是便放到了下一个项目中。
我在去年短暂 beach 过两次,每次大概一周左右:
第一次在年初,写了微前端框架 Mooa。
第二次在年中,我写了我的 markdown 编辑器 Phodit,作为我的第三本书《前端架构:从入门到微前端》 的编辑器。
## 为自己编程
在过去的一个月里,在 beach 的十几天里,我也不算太差,学了一些新的东西:
1. 出于某种目的,学了一下 Flutter,等我有空我写一个评测吧。
2. 重新学习了一下 React,尝试去搭建一下脚手架。
3. 在编写我的知识管理工具 Pholedge
顺带打个广告,其中的一个功能是 markdown 和思维导图的互转。
## 读书,学习
见下一篇文章(推荐几本书)。
## 思考下一步
TBC
## 其它
还有听了 Taylor Swift 的新专辑 《Lover》,以及注册了个抖音——分享一下日常编程。
## 结束了?
这些都不重要,Welcome to Join ThoughtWorks。个人知识管理的思考2019-07-29T12:34:29+00:002019-07-29T04:36:02.507263+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/thinking-in-personal-knowledge-manage/最近,我困扰于『知识管理』——其实一开始,我并不知道我需要的是它。在我打算买几本 Kindle 电子书,以消磨一下时间的时候,我发现了『知识管理』相关话题的电子书。我这才意识到,**我迫切需要一些新的工具来管理知识。**
一来,我接触的东西太多了,以致于忘记的速度太快了(反正比两分钟长)——而写一篇博客的时间太长了,打开一个网页两三分钟,我可能就会关闭它了。
二来,我所在的公司正在变成一家『大公司』。过去的扁平、透明也在逐渐的减弱,作为一个普通员工,我所能访问的资料也越来越有限。
不过,这些都不重要,重要的是让沉淀变得更多,变得更加高效。毕竟,随着年纪的增长,越来越多的时间被各种杂事占用了。当你一个人的时候,你有充足的时间来干你喜欢的事;当生活变成两个人的时候,你得陪另一半做喜欢做的事;当以后变成三个人的时候,你就有更多事情了。所以,在时间变得更加有限之前,我们所能做的事情变成了:高效。
当然了,所谓的低效是相对的。如果过了这么多年,我还是保持一样的知识管理效率,那么说明它是的低效——即有待提升。
## 现有的知识管理体系
在日常的学习和工作中,我们有不同的方式来摄取方方面面的知识,也应用于不同的领域。它并不局限于日常的技术工作,还有与软技能相关的知识,还有其它方方面面的内容。
为了优化这些知识吸收的过程,我们需要这么几步:
1. **记录**。记录现有的知识体系
2. **可视化**。呈现再有步骤的过程
3. **度量**。记录每个步骤所花的时间
4. **优化** 。寻找可优化的空间
作为一个刚入门知识管理领域的人,我只能先**记录**相关的步骤,再寻找可视化及度量的方式。
### 内容管理
对于写作来说,在确认了主题/目标之后,往往是分为这么几步进行的:
1. 收集资料。来源往往是 Google 搜索的相关博客、书籍等等。
2. 提炼资料。寻找文章、书籍的一些关键点。
3. 整理体系。根据需要整理第一步的内容。
4. 消化存档。嗯,就是一个手工活,或是变成文章,或只是一堆草稿。
而在真正实现的过程中,我感觉有两步并不是那么理想,即**收集资料**和**编写内容**。(PS:至于能不能优化是另外一件事)
- **收集资料**。随着时间的变化 ,我收集资料发生了一些改变。过去,在写作的时候,我只会留下一个链接 ,现在多了一个留言,甚至有可能会有专门的引用部分。这样一来,未来在回过头来看这些资料的时候,能找到一些思路。
- **编写内容**。在写作的过程中,我们消化了相关的内容,并提出了自己的一些新的看法。但是,并非所有的时候,都有必要去写一篇文章。有些内容,我们只需要用代码的方式来展示,诸如于 ``gist``,再配合一些注释就可以了。而有些内容,则只需要一个大纲就可以了,诸如之前的 New Project Checklists。
顺带一提,在我进行信息提炼的时候,使用最多的工具是,笔 + 纸 / Wacom Bamboo Slate(电子笔记本),主要是我的 iPad 太小,而 iPad Pro 又太贵了。当我们使用电脑的时候,由于习惯的原因,使用往往是线性的思维。而我们的思路往往都是非线性的。
### 阅读
尽管我阅读了很多的书,但是我在这方面的产出相当的少。而据我所知,一种颇为有效的方式是:
- **思维导图**。采用**思维导图**的方式来构建出这本书的内容。就我个人而言,这种方式失去了读书的乐趣,目的性太强,所以去不会采用
于是乎,我采用的仍然是很传统的方式:
- **记录笔记**。我一般看书的时候,会偶尔做一些笔记。只是这些笔记,缺少一个承载的地方。
- **书评**。最近,最近好像已经不怎么写相关的书评。大概是因为写这么一个书评,它所花费的时间比较多,有种投入产出比不划算的感觉。因为这本书是在我书架上存在的内容,我想要的时候都可以随时拿到。但是,当我的书在杭州,而人在上海,就会变成一种新的挑战。
但是,由于我看书比较随性,往往什么都没有做,这一点倒是有一些改进空间。我往往觉得大致能把目录记下来,下次就可以方便地阅读这本书了。反正,书放我的书架上不会跑。
### 知识图谱
对于知识的索引与图谱构建,大抵是我在过去最有收获的部分之一。即整理某一领域相关的知识,诸如于读书路线、学习路线、技能图谱。
往后,在学习的时候,就可以有针对性地查缺补漏。又或者是,为刚进入这个行业的新人提供一些建议。
### 编码
在我们工作中用得最多的就是,对于将知识放在代码中,每个项目都有多种多样的方式:
- **README**。
- **注释**。
- **提交信息**。
- **架构决策记录**。
- **架构图**。诸如于 C4 模型
对于大部分的内容来说,要实施起来并不困难。但是,如何在代码中体现业务逻辑呢?又或者是通过代码来生成业务逻辑?比如,通过注解生成 URL 跳转相关的路径?
而如果是业务相关的知识,则可能有不同的形式来实现:
- **测试用例**。
- **用户旅程**。
- **用户地图**。
设计初期,我们会拥有一个初步用户旅程。而完成项目的时候,用户旅程可能会发生一些变化,我们应该采用、开发工具去优化这个过程。它可以通过 URL 来记录页面,而后通过 DSL 来转换成旅程地图。
### 工作流程
在经历了一个又一个项目之后,我对项目的生命周期有了更好的理解。而项目的生命周期,也相当于是工作流程。对于流程来说,规范是一个很扯淡的东西——没有人想去看文档。在编码中,我们可以通过一些 hook 来规范工作,如:
- pre-commit
- pre-push
- lint
而对于非编码性工作来说,其应对的最好方式是:**开发流程的工具**。它并非是一个固化的流程,但是它拥有所的工具。这也是我正在尝试去做的东西之一。年初做的 New Project Checklists, 以及正在实施的升级版本:应用周期管理,都是一些新的尝试。
## 可改进方向
除了上面提到的一些点的相关优化,是否还能尝试更多的东西?
### 优化收集?
如上所说,我在收集的这一维度,还有一点改进的空间:
- **记录、标记内容**。诸如于,我会时常关注 GitHub Trending,又或者是收藏一些 GitHub 上的项目,但是我并没有很好地工具来管理它们。对于这样简单的一个工具,我可能得想办法自己动手制作一个,说简单也简单,说难也不难。
- **重启 RSS 订阅?**。不过,这也是我当前存在的一个疑惑。过去, 我能使用 RSS 工具来管理资源,它的前提是互联网是开放的。而今,通过 RSS 已经很难找到一些优质的博客——除了我的博客,哈哈。
尽管它会花费一定的时间,但是将来在使用的时候,可能会节省更多的空间。唯一的问题是,还得做个工具。
### 寻找非线性输出方式?
线性的内容对于来说,问题不大,因此非线性内容就成为一个关注的点。只是呢,我并没有一点儿头绪。不过,一些有趣的方向可以是:
- **图片**。画图更浪费时间,哈哈。
- **照片**。比如拍照的时候,进行文字识别,不过对应的识别 API 意味着额外的支出。可能 Tag 是一个更好的选择吧,haha。
- **思绪导图**。现有的思维导图工具,都是线性的,又或者是本地使用的。而我又不想把数据放置在别人的数据库上,万一数据被删了呢。
所以,还可以自制一个简单的工具,加上额外的扩展方式,才是我所需要的内容。
### 集中式管理?
集中管理,意味着:
1. 标准化的输出方式。
2. 可 API 访问的平台。
3. 集成内容中心。
出于搜索的需要,我们
- GitHub 上的项目
- 社区的技术文章
也因此,我们并不一定能做这样的时间,但是可以尝试结合**标签来关联网络**
### 模板化?
TBC。(我并不喜欢模板化)
## 相关工具
如下是我日常使用的知识管理工具:
- 笔记。以前我用的是 Evernote,但是它收费,我便改成了 Microsoft OneNote。我仍然也没用好它。
- 看板。对于一些开源项目来说,我会使用 Trello,GitHub Projects 来管理。
- Todo App。我习惯使用 Wunderlist,因为我已经习惯了。不过,我最近正在切换到 Microsoft To-Do,因为自 Wunderlist 被微软收购之后,To-Do 才是未来。
- Calender 工具。还没有。作为一个普通的开发者,我并没有那么忙,偶尔有一些就用手机自带的。
- Markdown + Git + ADR。ADR 是一个架构决策工具,我用它来管理开源项目中的 IDEA
- GitHub issues。用来管理新的 Ideas,见: [https://github.com/phodal/ideas/issues](https://github.com/phodal/ideas/issues)
不过,我并不习惯于使用看板这样的工具,对于我来说,它太重了——总有一种回到工作的感觉。而 Todo 应用则在点一下,完成一下的时候,有那种相当爽快的感觉。其它工具推荐:
- RSS。Feedly。不过,话说现今国内活跃的博客越来越少了。我博客的 RSS 是( [https://www.phodal.com/blog/feeds/rss/](https://www.phodal.com/blog/feeds/rss/) ),欢迎订阅。
- 番茄钟。我用了一段时间,发现对我没啥用,我是那种多动的人。
因为,我还没有那么忙,所以大部分的时候,我还是处于佛系的状态。
## 结论
你有什么与众不同的方式,能分享一下吗?Angular Router Reuse 的那些坑2019-07-12T14:58:00+00:002019-07-12T14:58:37.131924+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/issue-on-angular-router-reuse-strategy/RouteReuseStrategy(路由复用策略)是 Angular 框架提供的一种路由复用机制。它可以在用户切换页面的时候,暂存路由的状态,在返回路由的时候,恢复这些快照。
**使用场景**
我们在做一个后台管理系统,它使用的是 Tab 页 + 无限滚动的方式实现。一旦我们从这个列表页进入详情页,再返回列表页时。不仅会回到列表的最上面,而且还会重置 Tab 的状态。为此,我们的业务需求是:记错这个页面的状态。
于是乎,我使用了之前的:《[适用于 Angular Lazyload 下的 RouteReuseStrategy](https://www.phodal.com/blog/angular-2-lazyload-reuse-stragery/)》,然后出了一堆的 bug。
### 1. queryParams
我遇到的第一个坑是 queryParams 失效了——对于跳转过来的 URL 来说,原先的代码是:
```typescript
this.route.queryParams.subscribe(params => {
this.param1 = params['param1'];
this.param2 = params['param2'];
});
```
后来,临时改成了通过 ``queryString`` 来解决 URL 上的参数。
### 2. 全局 cache 问题
先前配置的 ``shouldAttach`` 是通过判断缓存中,是否存在对应的路由,即:
```typescript
/** 若 path 在缓存中有的都认为允许还原路由 */
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (!!route.data.reusePath && !!SimpleReuseStrategy.handlers[route.data.reusePath]) {
return true;
}
return false;
}
```
这样做导致了一个问题,不管从哪个页面过来,它会被 cahce。
**解决方式 1**:在组件中判断上一页是否是详情页,如果是则使用缓存,如果不是则重新加载数据。这是我在上上一个项目中采用的方式,但是这种方式太麻烦了,需要在每个组件中写上对应的逻辑。
**解决方式 2**:配置需要缓存的页面,结合 reusePath 使用。
相关的步骤如下:
1. 标记详情页。在对应的 componet 中添加一个全局可访问的静态变量,诸如于 ``componetName = 'BlogDetailComponent'``.
2. 在 ``shouldAttach`` 的时候,先判断是否是 reuse,再判断上一个页面是否是需要缓存的页面。
这样一来,我们就可以将主要的逻辑放置在 ``SimpleReuseStrategy``,而非每个 component 中。
```typesciprt
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
let cachablePage = false;
...遍历 this.cachedPages,判断是否等于 lastPageName
if (!!route.data.reusePath && !!SimpleReuseStrategy.handlers[route.data.reusePath] && cachablePage) {
return true;
}
this.lastPageName = route.routeConfig.component('componetName');
return false;
}
```
于是,又遇到新的坑。
### 3. 元素不销毁
采用 RouteReuseStrategy 时,页面只是被缓存,而没有被销毁。因为 Component 对就的 ngDestory 方法不会被销毁。
我们遇到的第一个坑是:无限滚动的 ``infiniteScroll`` 不会被 desotry。于是当我们在详情页的时候,仍然会触发 ``scrolled`` 方法,而解决的办法也很简单。在我们二次封装的组件里,在 ``ngOnInit`` 时,监听路由是否变化,然后调用 ``infiniteScroll`` 的 ``destoryScroller()``。
```typescript
this.router.events.subscribe((event) => {
if (event instanceof NavigationStart && this.scrollEl) {
this.scrollEl.destoryScroller();
}
})
```
除此,我们还有其它的组件也有同样的问题,解决方式是相似的。
### 其它方式
可以在我们的 ``SimpleReuseStrategy`` 类中,提供静态方法给组件使用。
```typescript
public static deleteRouteSnapshot(name: string): void {
if (AppRouteReuseStrategy.handlers[name]) {
delete SimpleReuseStrategy.handlers[name];
} else {
SimpleReuseStrategy.waitDelete = name;
}
}
```