2023/07/31

帕金森定理 (Parkinson's Law)

1955年 Cyril Northcote Parkinson 在 The Economist 發表一篇短文

Work expands so as to fill the time available for its completion.

在工作能夠完成的時限內,工作量會一直增加,直到所有可用時間都被填充為止

後來在 1958年,擴充為一本書:Parkinson's Law: The Pursuit of Progress

The demand upon a resource tends to expand to match the supply of the resource (If the price is zero).

在預算之內,支出的需求會一直增加,直到所有資源被用完為止

員工會製造很多瑣碎的工作,讓自己看起來很忙,就算時間充裕,也會放慢工作速度,或是找其他的事情做,直到預定的交付時間被填滿。後續又以工作量增加要求招僱更多員工,組織漸漸膨脹,但從外面看起來,大家看起來很忙,實際上是工作效率低下。

  1. 增加下屬法則

  2. 增加工作法則

為避免工作拖延的狀況,管理者可以安排更多工作,或是制定不合理的交付時間。但這樣的作法可能會有副作用:

  1. 期限內無法完成的事情會越來越多

  2. 為了在期限內完成,無法確保工作品質

  3. 員工因不堪負荷而離職

本定律要發生有四個條件

  1. 要有一個組織,管理階層在組織中佔有一定的地位

  2. 該管理者能力平庸,是個不稱職的管理者

  3. 對該管理者而言,可能因為某些事情而喪失權力

  4. 該組織是個不斷自我要求完善的組織,能夠不斷吸收新人

References

帕金森定理 - 維基百科,自由的百科全書

# 帕金森定律》為何公司的人越來越多,生產效率卻越來越差?

帕金森定律:如何克服它以提高生產力 • Asana

《每個人的商學院・管理基礎》:大企業最常犯的「帕金森定律」與「彼得原理」 - The News Lens 關鍵評論網

帕金森定律 - MBA智库百科

2023/07/24

隱式接口法則 (Hyrum's Law, The Law of Implicit Interfaces)

在 Google 開發 C++ library 的工程師 Hyrum Wright 在網站 https://www.hyrumslaw.com 提出一個軟體工程中觀察的經驗法則:

With a sufficient number of users of an API,
it does not matter what you promise in the contract:
all observable behaviors of your system
will be depended on by somebody.

當某個 API 有了相當多的使用者以後,
原本的規格已經不重要了:
因為所有可被發現的系統行為,
都會被某人使用,並依賴該系統行為。

API 是系統模組之間互相交互運作的介面,也是某個功能模組的抽象化,因為單一功能模組的內容細節過於複雜,無法讓所有人都理解細節,故使用者會透過抽象介面使用該功能。

隨著系統的使用者增加,會漸漸地透過實作的細節,慢慢披露出該模組的未公開的功能內容,導致有越來越多的使用者,依賴於該 API 的所有系統行為。也就是隱式接口法則。

隱式接口通常是慢慢發生的,使用者並不會意識到正在發生,例如:API 通常沒有性能保證,但使用者會期待該 API 能夠達到某個程度的運算能力,這表示 API 可能需要被慢慢修改到符合使用者的期待。

例如 Hash 的輸出順序,在原本的定義中,並不會保證有固定輸出的順序,但某些語言實作時,會發生固定輸出順序的狀況,導致有使用者套用這個規則使用 Hash。

References

海拉姆定律——一个软件工程的观察

软件工程中的海仑定律 - hyrumslaw

程序员应知必会的思维模型之 16 隐式接口定律 (Hyrum‘s Law or The Law of Implicit Interfaces)_知识大胖的博客-CSDN博客_hyrum's law

2023/07/17

赫特伯定律 Hutber's Law

Patrick Hutber 於 1970 年代提出 Hutber's Law

Improvement means deterioration.

改善等同於惡化。任何改善的細節中,都隱藏著可能的惡化因子。對於系統的某個部份的功能改進,可能會影響到其他部分,導致其他功能發生問題。

在對軟體進行改版,修改功能的時候,常會發生 side effect 副作用,有可能是功能本身定義的問題,因為該功能的定義修改,而造成其他功能的條件設定跟原本的不同了。也有可能是因為共用程式碼的關係,某個部份的修改,在另一個部分重用的時候,造成條件不符的問題。

但這種問題,常常很難在第一時間就被發現。

Test Driven 軟體開發方法,就是在開發時,要寫足夠多的測試程式碼,這些測試程式就可以用來預先發生這些可能的未知問題。用大量自動化的測試,來提醒開發人員無法預先知道的問題。

但在使用者介面測試這個部分,目前還比較不容易做自動化,目前是有自動化 UI 測試的工具,但要維護 UI Test 的成本太高,有些問題也無法用自動化測試察覺。

References

Hutber's law - Wikipedia

Hutber’s Law Explained - YouTube

2023/07/10

UUID

UUID: Universally Unique Identifier 通用唯一識別碼,是 128 位元的識別碼,被開源軟體基金會 (Open Software Foundation, OSF) 的組織使用在分散式計算環境 (Distributed Computing Environment, DCE)。用途是讓分散式系統中的元素,能有唯一的辨識資訊,不需要透過一個集中的系統辨識資訊的唯一性。2005年7月,RFC4122 定義了 UUID 的 URN name space,並制定 UUID 的規格。

結構

UUID 的結構為 16 個 8 bits 的 byte array,總共是 32 個 16 進位的數字,以 "8-4-4-4-12" 的形式表示

xxxxxxxx-xxxx-Bxxx-Axxx-xxxxxxxxxxxx

Variant

其中 A 代表 UUID variants 欄位,目前的規格使用 variant 2

Name binary bits description
Apollo NCS Variant 0XXX 1988 年 Apollo 系統使用
OSF DCE Variant 10XX RFC 4122/DCE 1.1 UUIDs
Microsoft COM/DCOM Variant 110X reserved, Microsoft Corporation backward compatibility
Reserved Variant 未定義 所有未定義的格式

B 代表 Version,以一整個 byte 的值代表 UUID 的版本

目前 Version 有 1,2,3,4,5 五種,最常使用的是隨機生成的 Version 4。

  • Time-Based (UUIDv1)
  • DCE Security (UUIDv2)
  • Name Based (UUIDv3 and UUIDv5)
  • Random (UUIDv4)

Version

Version 1

根據時間, Version, Variant 產生前四個部分的值,根據 Mac Address 產生 Node。這個版本的 UUID 優點是依照時間順序產生,因此排序速度比較快,但也比較有可能會產生相同的 UUID。

如果是真實的 MAC Address,可以追蹤到原始產生 UUID 的電腦,但這部分也可以用亂數產生。

98a956f7-0188-1000-88fc-6650c08fc392

98a956f7: Low Time
0188: Mid Time
1000: High Time and Version
88fc: Clock Sequence and Variant
6650c08fc392: Node

Version 2

比較少被使用,規格定義於 DCE 1.1: Authentication and Security Services - Privilege (Authorisation) Services

Version 3, 5

根據 namespace 及唯一的 name 產生,當輸入的字串固定時,產生的 UUID 也會是固定的。

Version 3 是使用 MD5 hash of the name and namespace

Version 5 是使用 SHA-1 hash of the name and namespace,當 hash 結果長度太長時,就直接 truncated

Version 4

完全是亂數產生的,有超過 5.3 x 10^36 個 UUIDs,這是最常被使用的版本

Java Code

import java.util.Random;
import java.util.UUID;

public class UUIDTest {
    public static void main(String... args) {
        v1();
        v3();
        v4();
    }

    private static void uuid_field(UUID uuid1) {
        int version = uuid1.version();
        int variant = uuid1.variant();
        int clockSequence = -1;
        long node = -1;
        long timestamp = -1;
        try {
            clockSequence = uuid1.clockSequence();
            node = uuid1.node();
            timestamp = uuid1.timestamp();
        } catch(UnsupportedOperationException e) {

        }
        System.out.println("uuid="+uuid1+", version="+version+", variant="+variant+", clockSequence="+clockSequence+", node="+node+", timestamp="+timestamp);
    }

    private static void v4() {
        UUID uuid1 = UUID.randomUUID();
        UUID uuid2 = UUID.randomUUID();
        UUID uuid3 = UUID.randomUUID();

        System.out.println("UUID v4");
        uuid_field(uuid1);
        uuid_field(uuid2);
        uuid_field(uuid3);
        System.out.println("");
    }

    private static void v3() {
        String s1 = "Test String";
        String s3 = "Test String3";
        UUID uuid1 = UUID.nameUUIDFromBytes(s1.getBytes());
        UUID uuid2 = UUID.nameUUIDFromBytes(s1.getBytes());
        UUID uuid3 = UUID.nameUUIDFromBytes(s3.getBytes());

        System.out.println("UUID v3");
        uuid_field(uuid1);
        uuid_field(uuid2);
        uuid_field(uuid3);
        System.out.println("");
    }
    // UUID.java 裡面的 v3 實作
//    public static UUID nameUUIDFromBytes(byte[] name) {
//        MessageDigest md;
//        try {
//            md = MessageDigest.getInstance("MD5");
//        } catch (NoSuchAlgorithmException nsae) {
//            throw new InternalError("MD5 not supported", nsae);
//        }
//        byte[] md5Bytes = md.digest(name);
//        md5Bytes[6]  &= 0x0f;  /* clear version        */
//        md5Bytes[6]  |= 0x30;  /* set to version 3     */
//        md5Bytes[8]  &= 0x3f;  /* clear variant        */
//        md5Bytes[8]  |= 0x80;  /* set to IETF variant  */
//        return new UUID(md5Bytes);
//    }

    private static void v1() {
        UUID uuid1 = generateUUID_V1();
        UUID uuid2 = generateUUID_V1();
        UUID uuid3 = generateUUID_V1();

        System.out.println("UUID v1");
        uuid_field(uuid1);
        uuid_field(uuid2);
        uuid_field(uuid3);
        System.out.println("");
    }

    public static UUID generateUUID_V1() {
        long most64SigBits = get64MostSignificantBitsForVersion1();
        long least64SigBits = get64LeastSignificantBitsForVersion1();
        return new UUID(most64SigBits, least64SigBits);
    }

    private static long get64LeastSignificantBitsForVersion1() {
        Random random = new Random();
        long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL;
        long variant3BitFlag = 0x8000000000000000L;
        return random63BitLong | variant3BitFlag;
    }

    private static long get64MostSignificantBitsForVersion1() {
        final long currentTimeMillis = System.currentTimeMillis();
        final long time_low = (currentTimeMillis & 0x0000_0000_FFFF_FFFFL) << 32;
        final long time_mid = ((currentTimeMillis >> 32) & 0xFFFF) << 16;
        final long version = 1 << 12;
        final long time_hi = ((currentTimeMillis >> 48) & 0x0FFF);
        return time_low | time_mid | version | time_hi;
    }
}

執行結果

UUID v1
uuid=98a956f7-0188-1000-88fc-6650c08fc392, version=1, variant=2, clockSequence=2300, node=112497014064018, timestamp=1686188414711
uuid=98a956fb-0188-1000-b9c6-84e9d3083144, version=1, variant=2, clockSequence=14790, node=146139802775876, timestamp=1686188414715
uuid=98a956fb-0188-1000-9bdc-540d86968ed3, version=1, variant=2, clockSequence=7132, node=92417069321939, timestamp=1686188414715

UUID v3
uuid=bd08ba3c-982e-3ad7-a860-2536fb8e1184, version=3, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=bd08ba3c-982e-3ad7-a860-2536fb8e1184, version=3, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=c1a59283-7a55-3e83-b9ed-864b5367685b, version=3, variant=2, clockSequence=-1, node=-1, timestamp=-1

UUID v4
uuid=2de4d27e-9abc-4b9d-83bd-b721ef6b2882, version=4, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=e82fca87-acd0-4000-8a4f-f4d7001d56a4, version=4, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=fb3a6890-57e3-4a13-a238-ef6f3de63fbe, version=4, variant=2, clockSequence=-1, node=-1, timestamp=-1

References

通用唯一辨識碼 - 維基百科,自由的百科全書

# 閒談軟體架構:UUID

# 閒談軟體架構:UUID 之三部曲

Guide to UUID in Java | Baeldung