Tuesday, May 13, 2025

代码注释的艺术,优秀代码真的不需要注释吗?

Here is the article. 

01

前言

Aliware


前天回家路上,有辆车强行插到前面的空位,司机大哥暴躁地拍着方向盘吐槽道“加塞最可恶了”,我问“还有更可恶的吗”,司机大哥淡定说道“不让自己加塞的”。似乎和我们很类似,我们程序员届也有这 2 件相辅相成的事:最讨厌别人不写注释,更讨厌让自己写注释。

一段糟糕的代码,往往大家最低的预期是把注释写清楚,最合理的做法通常应该对代码做优化。如果我们将代码真正做到了优秀,我们是否还需要注释?

02

注释的意义

Aliware


; **************************************************************************; * RAMinit Release 2.0 *; * Copyright (c) 1989-1994 by Yellow Rose Software Co. *; * Written by Mr. Leijun *; * Press HotKey to remove all TSR program after this program *; **************************************************************************; Removed Softwares by RI:; SPDOS v6.0F, WPS v3.0F; Game Busters III, IV; NETX ( Novell 3.11 ); PC-CACHE; Norton Cache; Microsoft SmartDrv; SideKick 1.56A; MOUSE Driver; Crazy (Monochrome simulate CGA program); RAMBIOS v2.0; 386MAX Version 6.01

注释是对代码的解释和说明,本质目的是为了增强程序的可读性与可解释性。注释会随着源代码,在进入预处理器或编译器处理后会被移除。这是雷布斯 1994 年写的一段 MASM 汇编代码,注释与代码整体结构都非常清晰。如果说代码是为了让机器读懂我们的指令,那注释完全就是为了让我们了解我们自己到底发出了哪些指令。

03

争议与分歧

Aliware


注释的起源非常早,我们甚至已经查阅不到注释的由来,但现在任何一种语言,甚至几乎任何一种文本格式都支持各式各样的注释形式。


但如何使用注释,其实一直是一个备受争论的话题。当我们接手一段‘祖传代码’时,没有注释的感觉简直让人抓狂,我们总是希望别人能提供更多的注释。但软件届也有一段神话传说,叫做『我的代码像诗一样优雅』。有注释的代码都存在着一些瑕疵,认为足够完美的代码是不需要注释的。

04

坏代码的救命稻草

Aliware


The proper use of comments is to compensate for our failure to express ourself in code.
-- Robert C. Martin 《Clean Code》
译:注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败


Clean Code 的作者 Robert C. Martin 可以说是注释的极力否定者了,他认为注释是一种失败,当我们无法找到不用注释就能表达自我的方法时,才会使用注释,任何一次注释的使用,我们都应该意识到是自己表达能力上的失败。


PH&V 的系统架构师和负责人 Peter Vogel,同样也是一名坚定的注释否定着,他发表了一篇文章 why commenting code is still bad 来表述为代码添加注释在某种程度上可能是必要的,但确实没有价值。

事实上,我们也确实经历着非常多无价值的注释,以及完全应由代码来承担解释工作的“职能错位”的注释。

01


零注释




糟糕的代码加上完全不存在的注释,我喜欢称呼它们为『我和上帝之间的秘密』,当然过 2 个月后也可以称之为『上帝一个人的秘密』。


压垮程序员最后一根稻草的,往往都是零注释。可以没有文档,可以没有设计,但如果没有注释,我们每一次阅读都是灾难性的。当我们抱怨它一行注释都没有时,其实我们是在抱怨我们很难理解代码想要表达的含义,注释是直接原因,但根本原因是代码。


零注释往往和坏代码一起生活,“没有注释”的吐槽,其实本质上直击的是那堆歪七扭八的英文字母,到底它们想表达什么!

02


无用注释


/** * returns the last day of the month * @return the last day of the month */public Date getLastDayOfMonth(Date date) {    Calendar calendar = new GregorianCalendar();    calendar.setTime(date);    calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));    return calendar.getTime();}

这是典型的废话注释,读代码时代码本身就能很好的表达具体的含义,我们完全不需要看注释,并且注释也不会给我们提供更多有效的信息。无用注释或许是零注释的另一个极端,我们担心自己写的代码被人所吐槽,于是尽可能去补全注释,当你为 getLastDayOfMonth() 补一段 get last day of month 的注释时,恭喜你,你得到了双倍的代码。

03

代码优于注释


"Comments Do Not Make Up for Bad Code"
-- Robert C.Martin 《Clean Code》
译:注释不能美化糟糕的代码


当需要为一段代码加上注释时,说明代码已经不能很好的表达意图,于是大家开始为这段代码添加注释。Robert C.Martin 在 Clean Code 中提出一个观点:注释不能美化糟糕的代码。能用代码表达的直接用代码表达,不能用代码表达的,你再想想,如何能用代码表达。


复杂的代码最直接的表现就是不够直观、难以理解,加上注释后往往会清晰很多,但你是愿意看这段代码:

// 判断是否活跃用户if((customer.getLastLoginTime().after(dateUtils.minusDays(new Date(),15)) && customer.getCommentsLast30Days() > 5)     || orderService.countRecentDaysByCustomer(customer,30) > 1)

还是这段代码?


if(customer.isActive())


糟糕代码的存在,通常是我们写注释的常见动机之一。这种试图粉饰可读性差的代码的注释称之为『拐杖式注释』,即使大名鼎鼎的 JDK,也存在这样的拐杖式注释。


public synchronized void setFormatter(Formatter newFormatter) {    checkPermission();    // Check for a null pointer    newFormatter.getClass();    formatter = newFormatter;}

这是取自 JDK java.util.logging.Handler 类的 setFormatter 方法,作者为了不让空指针异常下传,提前做一次空指针检查。没有这段注释我们完全不知道游离的这句 newFormatter.getClass() 到底要做什么,这段注释也充分表达了作者自己也知道这句代码难以理解,所以他加上了注释进行说明。但我们完全可以用 Objects.requireNonNull() 来进行替代。同样的代码作用,但可读性可理解性大不一样,JDK 里的这段代码,确实让人遗憾。

04

注释否定论


"If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much—perhaps not at all."
-- Robert C.Martin 《Clean Code》
译:若编程语言足够有表达力,或者我们长于用这些语言来表达意图,就不那么需要注释--也许根本不需要


通过代码进行阐述,是注释否定论的核心思想。当你花功夫来想如何写注释,让这段代码更好的表达含义时,我们更应该重构它,通过代码来解释我们的意图。每一次注释的编写,都是对我们代码表达能力上的差评,提升我们的归纳、表达、解释能力,更优于通过注释来解决问题。当代码足够优秀时,注释则是非必须的。并且需求在不断调整,代码一定会随之变动,但注释可能慢慢被人遗忘,当代码与注释不匹配时,将是更大的灾难。

05

软件设计的乌托邦

Aliware


01


好吧你很优秀


曾经我的确对优秀的代码不断钻研,对代码本身所蕴含的能量无比坚信。如同当科学代替鬼神论走上历史舞台时,即使存在有科学解释不了,我们依然坚信只是科学还需要发展。当代码别人无法理解时,我会认为是我表述不够精准,抽象不够合理,然后去重构去完善。


有一次给老板 review 代码,当时老板提出,“你的代码缺缺少注释”,我说不需要注释,代码就能自解释。于是老板现场读了一段代码,“query-customer-list 查询客户”、“transfer-customer-to-sales 分发客户到销售”、“check-sales-capacity 检查销售库容”,每一个类每一个函数,一个单词一个单词往外蹦时,你会发现好像确实都能读懂,于是老板回了一个“好吧”。

02


美丽的乌托邦


"'good code is self-documenting' is a delicious myth"
-- John Ousterhout《A Philosophy of Software Design》
译:‘好的代码自解释’是一个美丽的谎言


在软件设计中,总有一些软件工程师所坚信的诗和远方,有的是大洋彼岸的美好国度,有的或许是虚无缥缈的理想乌托邦。John Ousterhout 教授在 A Philosophy of Software Design 中提到一个观念,‘好的代码自解释’是一个美丽的谎言。


我们可以通过选择更好的变量名,更准确的类与方法,更合理的继承与派生来减少注释,但尽快如此,我们还是有非常多的信息无法直接通过代码来表达。这里的信息,或许不单单只是业务逻辑与技术设计,可能还包括了我们的观感,我们的体验,我们的接纳程度以及第一印象带来的首因效应。

06

好代码的最佳僚机

Aliware


You might think the purpose of commenting is to 'explain what the code does', but that is just a small part of it.The purpose of commenting is to help the reader know as much as the writer did.
译:你可能以为注释的目的是“解释代码做了什么”,但这只是其中很小一部分,注释的目的是尽量帮助读者了解得和作者一样多
-- Dustin Boswell《The Art of Readable Code》


如同 John Ousterhout 教授一样,The Art of Readable Code 的作者 Dustin Boswell,也是一个坚定的注释支持者。与 Robert C.Martin 类似,Dustin Boswell 同样认为我们不应该为那些从代码本身就能快速推断的事实写注释,并且他也反对拐杖式注释,注释不能美化代码。


但 Dustin Boswell 认为注释的目的不仅解释了代码在做什么,甚至这只是一小部分,注释最重要的目的是帮助读者了解得和作者一样多 。编写注释时,我们需要站在读者的角度,去想想他们知道什么,这是注释的核心。这里有非常多的空间是代码很难阐述或无法阐述的,配上注释的代码并非就是糟糕的代码,相反有些时候,注释还是好代码最棒的僚机。

01


更精准表述


There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton
译:计算机科学中只有两个难题:缓存失效和命名


Martin Fowler 在他的 TwoHardThings 文章中引用了 Phil Karlton 的一段话,命名一直都是一件非常难的事情,因为我们需要将所有含义浓缩到几个单词中表达。很早之前学 Java,接触到很长的类名是 ClassPathXmlApplicationContext。可能有人认为只要能将含义准确地表达出来,名字长一些无所谓。那如果我们需要有一段处理有关“一带一路”的内容,那我们的代码可能是这样的:


public class TheSilkRoadEconomicBeltAndThe21stCenturyMaritimeSilkRoad {
}

他非常准确的表达了含义,但很明显这不是我们期望的代码。但如果我们辅以简单的注释,代码会非常清晰,说明了简称,也说明了全意,表述更精准。

/** * 一带一路 * 丝绸之路经济带和21世纪海上丝绸之路 */public class OneBeltOneRoad {
}

02


代码层次切割


函数抽取是我们经常使用且成本最低的重构方法之一,但并非银弹。函数并非抽得越细越好,如同分布式系统中,并非无限的堆机器让每台机器处理的数据越少,整体就会越快。过深的嵌套封装,会加大我们的代码阅读成本,有时我们只需要有一定的层次与结构帮助我们理解就够了,盲目的抽取封装是无意义的。

/** * 客户列表查询 */public List queryCustomerList(){    // 查询参数准备    UserInfo userInfo = context.getLoginContext().getUserInfo();    if(userInfo == null || StringUtils.isBlank(userInfo.getUserId())){        return Collections.emptyList();    }    LoginDTO loginDTO = userInfoConvertor.convertUserInfo2LoginDTO(userInfo);    // 查询客户信息    List<CustomerSearchVO> customerSearchList = customerRemoteQueryService.query(loginDTO);    Iterable<CustomerSearchVO> it = customerSearchList.iterator();    // 排除不合规客户    while(it.hasNext()){        CustomerSearchVO customerSearchVO = it.next();         if(isInBlackList(customerSearchVO) || isLowQuality(customerSearchVO)){            it.remove();        }    }    // 补充客户其他属性信息    batchFillCustomerPositionInfo(customerSearchList);    batchFillCustomerAddressInfo(customerSearchList);}

其实细看每一处代码,都很容易让人理解。但如果是一版没有注释的代码,可能我们会有点头疼。缺少结构缺少分层,是让我们大脑第一感观觉得它很复杂,需要一次性消化多个内容。通过注释将代码层次进行切割,是一次抽象层次的划分。同时也不建议大家不断去抽象私有方法,这样代码会变得非常割裂,并且上下文的背景逻辑、参数的传递等等,都会带来额外的麻烦。

03

母语的力量


其实上述例子,我们更易阅读,还有一个重要的原因,那就是母语的力量。我们天然所经历的环境与我们每天所接触到的事物,让我们对中文与英文有完全不一样的感受。我们代码的编写本质上是一个将我们沟通中的“中文问题”,翻译成“英文代码”来实现的过程。而阅读代码的人在做得,是一件将“英文代码”翻译成“中文表述”的事情。而这之中经过的环节越多,意思变味越严重。


TaskDispatch taskDispatch = TaskDispatchBuilder.newBuilder().withExceptionIgnore().build();taskDispatch        // 外贸信息        .join(new FillForeignTradeInfoTask(targetCustomer, sourceInfo))        // 国民经济行业、电商平台、注册资本        .join(new FillCustOutterInfoTask(targetCustomer, sourceInfo))        // 客户信息        .join(new FillCustomerOriginAndCategoryTask(targetCustomer, sourceInfo))        // 客户扩展信息        .join(new FillCustExtInfoTask(targetCustomer, sourceInfo))        // 收藏屏蔽信息        .join(new FillCollectStatusInfoTask(targetCustomer, sourceInfo, loginDTO()))        // 详情页跳转需要的标签信息        .join(new FillTagInstanceTask(targetCustomer, sourceInfo, loginDTO()))        // 客户信息完整度分数        .join(new FillCustomerScoreTask(targetCustomer, sourceInfo))        // 潜客分层完整度        .join(new FillCustomerSegmentationTask(targetCustomer, sourceInfo))        // 填充操作信息        .join(new FillOperationStatusTask(targetCustomer, sourceInfo, loginDTO))        // 认证状态        .join(new FillAvStatusTask(targetCustomer, loginDTO))        // 客户地址和组织        .join(new FillCompanyAddressTask(targetCustomer, loginDTO))        // 违规信息        .join(new FillPunishInfoTask(targetCustomer, sourceInfo))        // 填充客户黑名单信息        .join(new FillCustomerBlackStatusTask(targetCustomer, sourceInfo))        // 填充客户意愿度        .join(new FillCustIntentionLevelTask(targetCustomer, sourceInfo));        // 执行        .execute();

这是一段补齐客户全数据信息的代码,虽然每一个英文我们都看得懂,但我们永远只会第一眼去看注释,就因为它是中文。并且也因为有这些注释,这里非常复杂的业务逻辑,我们同样可以非常清晰的了解到它做了哪些,分哪几步,如果要优化应该如何处理。这里也建议大家写中文注释,注释是一种说明,越直观越好,中文的亲和力是英文无法比拟的。当然,这条建议并不适合美国程序员。

07

注释的真正归属

Aliware

01


复杂的业务逻辑


// Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {    throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {    // Not found -> check parent.    String nameToLookup = originalBeanName(name);    if (args != null) {        // Delegation to parent with explicit args.        return parentBeanFactory.getBean(nameToLookup, args);    }    else {        // No args -> delegate to standard getBean method.        return parentBeanFactory.getBean(nameToLookup, requiredType);    }}

这是 Spring 中的一段获取 bean 的代码,spring 作为容器管理,获取 bean 的逻辑也非常复杂。对于复杂的业务场景,配上必要的注释说明,可以更好的理解相应的业务场景与实现逻辑。

截取自:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean


02


晦涩的算法公式


/** * Returns the value obtained by reversing the order of the bits in the * two's complement binary representation of the specified {@code long} * value. */public static long reverse(long i) {    // HD, Figure 7-1    i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;    i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;    i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;    i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;    i = (i << 48) | ((i & 0xffff0000L) << 16) |        ((i >>> 16) & 0xffff0000L) | (i >>> 48);    return i;}

这是 JDK 中 Long 类中的一个方法,为 reverse 方法添加了足够多的注释。对于几乎没有改动且使用频繁的底层代码,性能的优先级会高于可读性。在保证高效的同时,注释帮助我们弥补了可读性的短板。

截取自:
java.lang.Long#reverse


03


不明所以的常量

/** * The bin count threshold for using a tree rather than list for a * bin.  Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. */static final int TREEIFY_THRESHOLD = 8;

这是 JDK 中 HashMap 的一个常量因子,记录由链表转向红黑树的链表长度阈值,超过该长度则链表转为红黑树。这里记录了一个 8,不仅记录了该常量的用途,也记录了为什么我们定义这个值。经常我们会发现我们代码中存在一个常量等于 3、等于 4,有时我们不知道这些 3 和 4 是干什么的,有时我们不知道为什么是 3 和 4。
截取自:
java.util.HashMap#TREEIFY_THRESHOLD


04


意料之外的行为


for (int i = 0; i < 3; i++) {    // if task running, invoke only check result ready or not    Result result = bigDataQueryService.queryBySQL(sql, token);    if (SUCCESS.equals(result.getStatus())) {        return result.getValue();    }    Thread.sleep(5000);}

代码及注释所示为每 5 秒 check 一下是否有结果返回,远程服务将触发与获取放在了一个接口。没有注释我们可能认为这段代码有问题,代码表现的含义更像是每 5 秒调用一次,而非每 5 秒 check 一次。为意料之外的行为添加注释,可以减少对代码的误解读,并向读者说明必要的背景及逻辑信息。

05

接口对外 API


/** * <p>Checks if a CharSequence is empty (""), null or whitespace only.</p> * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.</p> * StringUtils.isBlank(null)      = true * StringUtils.isBlank("")        = true * StringUtils.isBlank(" ")       = true * StringUtils.isBlank("bob")     = false * StringUtils.isBlank("  bob  ") = false * * @param cs  the CharSequence to check, may be null * @return {@code true} if the CharSequence is null, empty or whitespace only */public static boolean isBlank(final CharSequence cs) {    final int strLen = length(cs);    if (strLen == 0) {        return true;    }    for (int i = 0; i < strLen; i++) {        if (!Character.isWhitespace(cs.charAt(i))) {            return false;        }    }    return true;}

我们经常使用的 StringUtils 工具类中的 isBlank 方法,写了非常详情的注释,不仅包括方法的逻辑,入参的含义,甚至还包括具体示例。我们平常定义的二方库中的 HSF、HTTP 接口定义,同样需要有清晰详尽的注释,这里的注释甚至经常会多过你的代码。

截取自:
org.apache.commons.lang3.StringUtils#isBlank


06


法律文件信息


/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements.  See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License.  You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */

与法律相关的注释,在开源软件库中较经常遇到。涉及到一些版权及著作声明时,我们需要在源文件顶部放置法律相关注释。当然,我们不需要将所有法律信息写到注释中,如例子中的跳链,引用一份标准的外部文档,会是一个更好的选择。

08

写在最后

Aliware


注释并不会妨碍你写出优雅简洁的代码,它只是程序固有的一部分而已。我们不用过分在意我们的代码是否可以脱离注释,也不需要强调因为我们的代码符合什么原则,满足什么约定,所以代码是优秀的注释是冗余的。代码是一门艺术,并不会因为满足三规九条它就一定完美,因为艺术,是不可衡量的。

参阅书籍
《A Philosophy of Software Design》
《Clean Code》
《The Art of Readable Code》

《The Art of Readable Code》速查

Here is the article. 

Head First 系列书有一个特殊的章节:“there are no dump questions"。这个章节给我的启发是:没想透一些看起来很“傻”问题,你对问题所针对的事物的理解就还不够透彻(比如,苹果为什么会掉下来)。《The Art of Readable Code》这本书中,列了很多问题,看起来像是“傻问题”,并详细分析,从而让我们对什么是“可读”有了更深的理解。本文摘出书中最重要的 关键思想 及一些解读,从而让已全面翻阅此书的同学能快速想到相关内容。

可读性的基本定理

代码的写法应当使别人理解它所需的时间最小化。

表面层次优化

审美与设计

一致的风格比“正确”的风格更重要

该写什么注释

注释的目的是尽量帮助读者了解得和作者一样多

写出言简意赅的注释

注释应当有很高的 信息/空间 率

以上为表面层次优化部分的 关键思想 。由关键思想为原则出发,可以指导我们做出具体决策,简言之,代码也需要信达雅。高效地传达所有必需的信息给代码阅读者。高效  需考虑代码和注释的段落美观一致,言辞的精简准确,且不冗余(常见的冗余是注释与代码重复表达同一信息)

简化循环和逻辑

让控制流变得易读

把条件,循环以及其他对控制流的改变做得越“自然”越好,运用一种方式使读者不用停下来重读你的代码。当你对代码做改动时,从全新的角度审视它,把它作为一个整体来看待。(避免让原代码的控制流程易读性变差)

拆分超长的表达式

把你的超长表达式拆分成更容易理解的小块 要小心“智能”的小代码块——它们往往在以后会让别人读起来感 到困惑(条件判断中的短路逻辑有时会大大增加理解成本)

变量与可读性

让你的变量对尽量少的代码可见(因为人的思维栈也是有限的,过多变量在栈中存在是理解负担) 操作一个亦是的地方越多,越难确定它的当前值(尽量收拢变量的写逻辑,让它在更少的地方被改动)

确认这部分所说的 Bad Smell 的最好办法是,当你读到一个控制逻辑时,要反反复复地阅读,甚至用笔来记录时才能避免理解出错时,就意味着它需要修改了。具体如何修改,可以看书中的介绍

重新组织代码

抽取不相关的子问题

把一般代码和项目专有的代码分开

一次只做一件事

如果你有很难读的代码,尝试把它所做的所有任务列出来。其中一些任务可以很容易变成单独的函数(或类)。其他的可以简单地成为一个函数中的逻辑“段落”。

把想法变成代码

先用自然语言描述程序(debugging duck)

少写代码

最好读的代码就是没有代码(You aren't gonna need it, xp 的 YAGNI 原则) 从项目中消除不必要的功能,不过度设计 重新考虑需求,解决版本最简单的问题经常性通读标准库 API,保持对它们的熟悉程度

这部分设计函数级甚至类,需求分析级别的话题。在现实环境中,通常需求分析沟通是非“技术向”的事情。本部分“技术向”的部分是:让函数一次做且只做一件事,通过自然语言描述让代码逻辑更清晰,通常熟练掌握库 API ,避免重新发明轮子,字不必要的代码

精选话题

测试与可读性

测试应当具有可读性,以便其他程序员可以舒服地改变或增加测试 选择一组最简单的输入,让它能完整使用被测试代码 简单又能完成工作的测试值最好(测试代码中的输入或预期结果越简单越容易理解) 不能为可测而牺牲真实代码的可读性(而应对真实代码进行切实的改造,使之在有可读性的基础上可测)

这一部分读了可测性还有一章练习题可加深理解

附录:深入阅读

此部分介绍了一些经典书籍,去除一些和具体语言相关的,摘录如下,

  • 《Code Complete》中译:代码大全

  • 《Refactoring: Improving the Design of Existing Code》中译:重构:改进既有代码的设计

  • 《The Practice of Programming》中译:程序设计实践

  • 《The pragmatic Programmer: From Journeyman to Master》中译:程序员修炼之道:从小工到大师

  • 《Clean Code: A Handbook of Agile Software Craftsmanship》中译:代码整洁之道

  • 《Literate Programming》文学式编程


推荐书籍:《可阅读代码的艺术》

Here is the article. 

可阅读代码的艺术

书中讲了很多编程的小技巧,比如变量名就是最好的注释、尽量减少变量的作用域等等,这些虽然是很小的方面,但当你写了一个几K甚至几W行的程序时,你就会发现这些小细节会拖累你,想想各种变量纵横交错,让你的开发速度越来越慢,bug越来越多,经历这些日子简直就是噩梦。


另外可读不是说给别人读,可读很多时候都是你自己在读,你写一个大程序,每次开工之前总是会先看看附近的代码在干什么,有些东西写完放一边,日后用到也要回来看看,程序越大,你回去看以前代码的时间就会越来越多,想一想,程序越大,你每写一段程序,涉及到之前的代码也就越多,你不可能把所有的代码都放在脑子里面,所以就得读下看这段之前写的东西是什么。


有些细节无关紧要那就按照你自己觉得最好的方式进行,不一定要按照书上来。读此书的建议就是写过比较大的程序,之后你将不会再忽略这些细节,乖乖处理好,而不是狂妄或者无知地想你的大脑容量很大,日后一定处理得好。


网上找到的书籍只有英文版(但是我感觉比较容易读),叫《The Art of Readable Code》



China Lifts Restrictions on Boeing Plane Deliveries

 

China Lifts Restrictions on Boeing Plane Deliveries

In a trade truce with the U.S., Beijing tells Chinese airlines that they can resume taking delivery of pre-existing jet orders

Monday, May 12, 2025

华尔街日报:川普家族的加密货币业务正招致麻烦

 

华尔街日报:川普家族的加密货币业务正招致麻烦

文章来源:  于  - 新闻取自各大新闻媒体,新闻内容并不代表本网立场!
川普政府将监管川普家族正在销售的代币。

川普总统在处理家族企业问题上向来喜欢冒险,而在他第二任期内,这种冒险的程度比以往更甚。难道没有人告诫川普先生,他家族的加密货币计划正在招致麻烦?

川普先生上个月表示,他将于5月22日在华盛顿特区地区的高尔夫俱乐部为持有其$TRUMP迷因币的220位顶级持有者举办晚宴。只需投资川普加密货币企业,就有机会与这位自由世界领袖近距离接触。
总统声称这一切都合法合规。但这仍然引发了销售接触总统机会的利益冲突表象。

***

川普组织及其附属机构持有80%的$TRUMP代币,这些代币需经过三年解锁期。任何因疯狂买入而导致的价格升值都会增加川普家族的财富,至少在账面上是如此。

根据区块链公司Chainalysis的数据,川普家族企业及其合作伙伴已从该代币交易中获得超过3亿美元的收入。每笔交易的一定比例都被转入与该代币创建者相关的加密钱包,类似于交易费。

当川普先生在就职典礼前几天发行其迷因币时,他表示其目的是"找乐子"。正如我们当时指出的,这一冒险举动会带来政治风险和道德冲突。

彭博新闻上周报道,在网站排行榜上注册的$TRUMP代币25位顶级持有者中,除6位外,所有人都使用声称排除美国客户的外国交易所购买了这些代币。

这表明大多数买家来自海外。外国人被禁止向美国政治竞选活动捐款,但他们购买政治人物企业的股票并无限制。这实际上就是购买川普代币的外国买家正在做的事情。

某些人可能是试图向川普政府示好?

还有川普家族的另一个加密货币业务——World Liberty Financial,自去年10月以来通过代币销售已筹集5.5亿美元。川普先生及其家族成员所属的商业实体拥有World Liberty的大部分股权。
与大多数加密货币不同,其代币不能公开交易,尽管代币所有者据称可以对公司治理进行投票。

总部位于阿联酋的加密公司DWF Labs上个月宣布购买了价值2500万美元的World Liberty Financial代币。"因为这笔交易,我们在美国的知名度得到了提升,"DWF Labs的一位管理合伙人告诉《纽约时报》。"我们希望与政策制定者直接对话。"

毫无疑问。

作为川普政府外交特使史蒂夫·威特科夫之子的扎克·威特科夫已构建World Liberty Financial的联合创始架构,并主导其运营管理体系构建。此种背景结构是否为其创设了一条通过父亲(作为企业另一联合创始人的身份)建立所谓"政策对话"的隐性渠道?值得深度审视。扎克·威特科夫近期对外发布信息称,阿布扎比主权实体已确认将动用World Liberty Financial新近发行的数字稳定币,向全球知名加密货币交易平台Binance注入高达20亿美元的战略投资。

该战略性资本配置将产生双重效应:一方面为川普背书的数字稳定币提供市场推动力,同时为World Liberty金融生态系统注入可观的投资流动性。需引起重视的是,作为交易对手方的Binance已于2023年度正式向美国司法体系认罪,承认其违反了美国反洗钱法规框架及相关制裁条例。更具指标性意义的是,Binance核心创始人赵长鹏本人亦已对违反《银行保密法》这一严重金融监管违规行为作出司法认罪。多方媒体渠道近期报道显示,赵长鹏正通过多种渠道积极寻求获取美国总统特赦权,以期缓解其法律处境。

扎克·威特科夫上个月还会见了巴基斯坦总理谢赫巴兹·谢里夫和陆军参谋长阿西姆·穆尼尔。随后,World Liberty Financial宣布与巴基斯坦政府达成协议,加速该国的加密货币采用。

川普家族兜售加密货币的行为尤其不明智,因为川普政府将监管加密产品和实践。民主党人已经指责证券交易委员会对川普加密业务睁一只眼闭一只眼。

周四,民主党人阻止了参议院对创建稳定币监管框架的两党立法进行表决。加密行业支持该法案,但现在民主党人要求更严格的规则和执法。

预计媒体将在未来四年追踪川普加密家族的新闻。政治形象极其糟糕,特别是在川普边境税的背景下。民主党中期选举广告不言自明:"川普家族获得数百万秘密加密利润,但川普先生著名的'两个洋娃娃'和'五支铅笔'给其他所有人。"

读者诸君对克林顿政府时期政治献金者获准入住白宫林肯卧室的历史争议案例当有印象。纵观近期政治话语,川普先生曾以严厉批判姿态指责亨特·拜登行为——尤其针对其积极招揽海外商业资本并提供与时任总统乔·拜登接触渠道的行为模式。同样不容忽视的是亨特将其艺术作品售予身份不明购买方的交易行为,此类案例均构成政商伦理边界探讨的关键素材。

本专栏曾严厉批评那些政治敛财行为,共和党人也是如此。川普家族的加密货币业务同样看起来像一场等待发生的政治事故。

2025年5月12日印刷版以《The Trump Family Crypto Business》为题发表。

LEO trades | May 12 2025

 



May 9 

9:53 AM 
SOLD SMCZ $17.3 

11:34 AM
Plan to add GGLL from $26.8 - $26.85

12:54 PM
Bought AFRM $36.25

11:40AM
Bought PDYN $6.22

3:03 PM
Sold NBIS $31.33 Sold part of the position
4/16 bought $20.5
Mon 5/5 bought $23.85 Add more

3:56 PM
Sold NXTT $5.22

bought GGLL

May 11 

8:39 PM
PLTR $120.3 + 2.56% 
PLTU $50.9 + 5.2% 
Last Wed $40.7  - big gain 

8:48PM

HIMZ $40.41 + 10.4% big gain

May 12 

5:08AM
AFRM $39.75 +7.17%
Last Friday bought AFRM $46.25

5:22 AM
HIMZ $42.21 + 15.64%
Friday bought $31.9 + 32.32%

Sold EXPE $169.04 + 7.9% 

7:31 AM PST

Sold TSLL 12.23 meeting - before meeting

GGLL $28.8 + 7.16%, last Friday - added position   7:40 AM

Bought LABU $45.3 meeting before 

AAPU $23.78 + 10.08%

AAPL $211.27 + 6.42% 

May 12 sold AFRM $53.15 + 14.53% 8:58 AM

SOLD CONL



SOUN stock | 4.1 days -


 


Score one for SoundHound AISOUN $11.07 (22.72%), as the small-cap software company — and dreamboat of retail traders last year — soared Monday during an apparent short squeeze.

We say apparent, of course, because it’s impossible to conclusively say why any stock is moving at any particular moment.

But with no real news out for SoundHound Monday and the shares up roughly 20%, the massive amount of short interest in the stock (which we’ve spotlighted previously) clearly comes in for consideration as the catalyst.

As a refresher, short squeezes occur when short sellers — traders who borrow a stock, sell it, and hope to repurchase it at a lower price — are surprised when the shares actually rise. They then rush, en masse, to buy the stock, adding to upward momentum on prices and creating exaggerated price movements.

At last glance, stock out on loan to short sellers accounted for more than 30% of the company’s tradable float, a whopping indication the company, which for much of the last year dealt with lingering questions over its accounting practices, continues to face scrutiny from the market.

Tariff update | Ggll stock | EXPE stock | SABR stock | AFRM stock

 Evaluate difference among difference trades:

Tariff update | Ggll stock | EXPE stock | SABR stock | AFRM stock 

Ggll - 7.5% up on May 12

EXPE - 6.76% on May 12

SABR stock - 11.28% on May 12

AFRM stock - 15.79% on May 12 



Coreweave | CRWV | Short squeeze

#ChartExchange #BorrowRate #ShortSqueeze #CRWV 

 Luke Kawa

5h

Expensive bets against CoreWeave are getting smashed as shares soar

CoreWeaveCRWV $58.59 (13.84%) is behaving like it’s a twice-levered version of a US retailer that sources from China and just got major tariff relief.

Shares of the recently IPO’d cloud computing company are going bananas today, up 15%, ahead of its inaugural earnings report as a publicly traded firm on Wednesday after the close.

Like SoundHound AISOUN $11.07 (22.72%), this has the fingerprints of a short squeeze, with an extra dose of strong appetite for upside in the options market.

Exchange data shows 30% of CoreWeave’s float was sold short heading into the start of May, during which time it’s rallied more than 40%. The stock is also fairly expensive to borrow, with an annual rate of about 8.3%, per Interactive Brokers data. That’s the third-highest borrow rate among stocks with a market cap of at least $20 billion, per ChartExchange.

Fintech | Street Notes | Affirm’s Weak Guidance Hits the Stock. Why This Analyst Upgraded the Shares.

Affirm’s Weak Guidance Hits the Stock. Why This Analyst Upgraded the Shares.

Updated May 09, 2025, 12:51 pm EDT / Original May 09, 2025, 8:01 am EDT 

Affirm Holdings stock plunged on Friday after the buy now, pay later company issued weak fiscal fourth-quarter guidance. However, one analyst believes Affirm’s “various lanes of growth” will boost the share price even higher.

While Affirm’s fiscal third-quarter earnings topped analysts’ estimates, a conservative outlook for the current quarter appeared to be weighing on shares, which had fallen 13% in afternoon trading. The benchmark S&P 500 was down 0.2%. PayPal Holdings one of Affirm’s payment processing peers, was down 0.3%.

Management guided for fourth-quarter revenue in the range of $815 million to $845 million. Analysts polled by FactSet had forecast revenue of $841.4 million before earnings were released, above the midpoint of the range.

Even as shares tumbled, Susquehanna analyst James Friedman upgraded the stock to Positive from Neutral with a $65 price target. He noted that through Thursday’s close Affirm shares have fallen 33% from their peak in February and remain down 13% this year.