第 1 章 初入行当 1
1.1 在实战中,什么最重要? 2
1.2 谁是实战程序员? 3
1.3 杰出实战程序员 4
1.3.1 懂得质疑 4
1.3.2 结果驱动 5
1.3.3 高产出 6
1.3.4 接受复杂性和模糊性 6
1.4 现代软件开发存在的问题 6
1.4.1 技术繁多 8
1.4.2 遍阅范式 8
1.4.3 科技黑箱 9
1.4.4 低估开销 10
1.4.5 自扫门前雪 10
1.4.6 憎恶重复 11
1.5 特别说明 11
1.6 本书主题 11
本章总结 12
第 2 章 实用的理论 13
2.1 算法速成 14
2.1.1 要有好的 Big-O 16
2.2 深入数据结构 17
2.2.1 字符串 18
2.2.2 数组 21
2.2.3 列表 22
2.2.4 链表 23
2.2.5 队列 24
2.2.6 字典 24
2.2.7 哈希集合 26
2.2.8 栈 26
2.2.9 调用栈 27
2.3 类型有大用 28
2.3.1 使用强类型 28
2.3.2 有效性证明 29
2.3.3 巧用框架 34
2.3.4 用类型防止打错字 37
2.3.5 null 的可与不可 38
2.3.6 免费的更好性能 44
2.3.7 引用类型与值类型 45
本章总结 48
第 3 章 有用的反模式 50
3.1 若无损坏,亦可破坏 51
3.1.1 面对代码刚性 51
3.1.2 快刀斩乱麻 52
3.1.3 敬畏边界 53
3.1.4 隔离相同功能 54
3.1.5 网页示例 56
3.1.6 不要留下技术债 57
3.2 从头开始写 57
推倒重写 58
3.3 修复它,即使它没有坏掉 59
3.3.1 奔向未来 59
3.3.2 整洁仅次于功能 60
3.4 重复你自己 62
复用还是直接复制? 66
3.5 是我所创 67
3.6 不要使用继承 70
3.7 不要使用类 72
3.7.1 enum 太好用了! 72
3.7.2 结构体真棒! 74
3.8 写点糟糕代码 79
3.8.1 不要使用 If/Else 79
3.8.2 使用 goto 81
3.9 不写代码注释 84
3.9.1 选个好名字 85
3.9.2 充分利用函数 86
本章总结 88
第 4 章 美味的测试 89
4.1 测试的类型 90
4.1.1 手动测试 90
4.1.2 自动化测试 91
4.1.3 执意玩火:在生产环境中测试 91
4.1.4 选择正确的测试方法 92
4.2 如何停止抱怨,爱上测试? 94
4.3 不要使用 TDD 或其他缩写 100
4.4 为你自己的目的写测试 101
4.5 决定测试对象 102
4.5.1 尊重边界 103
4.5.2 代码覆盖率 105
4.6 不要写测试 107
4.6.1 不要写代码 107
4.6.2 不要一次写完所有的测试 107
4.7 让编译器测试你的代码 108
4.7.1 消除 null 检查 108
4.7.2 消除范围检查 111
4.7.3 消除有效值检查 113
4.8 命名测试 115
本章总结 116
第 5 章 正名重构 117
5.1 为什么我们要重构? 118
5.2 架构修改 118
5.2.1 识别组件 121
5.2.2 评估工作量和风险 122
5.2.3 树立威信 122
5.2.4 重构让重构更容易 124
5.2.5 最后冲刺 130
5.3 可靠重构 130
5.4 什么时候不重构 132
本章总结 133
第 6 章 安全审查 134
6.1 黑客之外 135
6.2 威胁模型 136
袖珍威胁模型 137
6.3 编写安全的网络应用程序 140
6.3.1 在设计时考虑到安全问题 140
6.3.2 隐蔽性安全的用处 141
6.3.3 不要光靠你自己去实现安全 142
6.3.4 SQL 注入攻击 142
6.3.5 跨站脚本攻击 148
6.3.6 跨站请求伪造 152
6.4 引发第 一次“洪水” 153
6.4.1 不要使用验证码 153
6.4.2 验证码的代替品 154
6.4.3 不要使用缓存 155
6.5 存储机密信息 155
保存源代码中的机密信息 156
本章总结 161
第 7 章 死磕优化 163
7.1 解决该解决的问题 164
7.1.1 简单的基准测试 164
7.1.2 性能与响应性 167
7.2 迟缓的剖析 168
7.3 从头开始 169
7.3.1 嵌套循环 170
7.3.2 面向字符串的编程 172
7.3.3 评估 173
7.4 打破瓶颈 174
7.4.1 不要打包数据 174
7.4.2 就地取材 175
7.4.3 将依赖性工作分开 176
7.4.4 要有可预测性 177
7.4.5 SIMD 179
7.5 I/O 的 1 秒与 0 秒 181
7.5.1 让 I/O 更快 181
7.5.2 避免 I/O 阻塞 183
7.5.3 古老的方式 184
7.5.4 现代式 async/await 185
7.5.5 异步 I/O 的弊端 186
7.6 如果所有方法都失败了,试试缓存吧 187
本章总结 187
第 8 章 可口扩展 188
8.1 不要使用锁 189
双重检查的锁 195
8.2 拥抱不一致 198
可怕的 NOLOCK 198
8.3 不要缓存数据库连接 200
以 ORM 的形式 203
8.4 不要使用线程 203
8.4.1 异步代码的问题 207
8.4.2 异步多线程 208
8.5 尊重单体 208
本章总结 209
第 9 章 与 bug 共存 210
9.1 不要修复 bug 211
9.2 错误恐惧 212
9.2.1 有关异常的真相 213
9.2.2 不要捕捉异常 215
9.2.3 容异性 217
9.2.4 没有事务的容异性 221
9.2.5 异常与错误 221
9.3 不要调试 223
9.3.1 printf()调试法 224
9.3.2 初识转储 225
9.3.3 高阶小黄鸭调试法 228
本章总结 228