Długie i krótkie funkcje a specjaliści i pseudo specjaliści :-)

2

Przez ostatnie kilka dni siedziałem głęboko w budowaniu różnych tuneli i VPN'ów między serwerami. Po drodze musiałem przebrnąć przez różne zakamarki linux'ów, wiele poczytać i pozgłębiać tematykę różnych rodzajów komunikacji. Podczas tej ciekawej przygody natknąłem się na oprogramowanie o nazwie WireGuard ... Nie zgłębiając do czego służy bo nie o tym chcę pisać doszło do sytuacji, w której wyniknęła potrzeba pobrania jego źródeł. Zacząłem je przeglądać i naszła mnie pewna refleksja ...
Czy to Oprogramowanie pisali amatorzy czy profesjonaliści?
Jeszcze kilak dni temu był na forum gorący temat o długości funkcji, w którym niektórzy z mocą "domestosa" głosili jakie to funkcje muszą być krótkie bo inne to się źle odpluskwia, źle czyta i w ogóle samo zło z tego wynika ... Tymczasem

https://git.zx2c4.com/wireguard-linux/tree/drivers/net/wireguard/noise.c

albo w pliku main na dzień dobry używają Go to :

Toż to trzeba tam natychmiast inkwizycję wysłać. Cóż za antywzorce. Jak z tym żyć ?
Tymczasem sam Pan Linus Torvalds powiedział, że ten kod to "dzieło sztuki" - no ale on to się pewnie na pisaniu w c nie zna tak jak forumowi ewangeliści.

"WireGuard's design seeks to reduce these issues, aiming to make the tunnel more secure and easier to manage by default. By using versioning of cryptography packages, it focuses on ciphers believed to be among the most secure current encryption methods, and at the time of the Ars Technica review had a codebase of around 4000 lines of kernel code, about 1% of either OpenVPN or IPsec, making security audits easier, and praised by the Linux kernel creator Linus Torvalds compared to OpenVPN and IPsec as a "work of art"." ( https://en.wikipedia.org/wiki/WireGuard )

Moje zdanie na ten temat jest takie jak zawsze ... Bezmyślne stosowanie nawet najlepszych wzorców tylko dlatego, że są popularne to przejaw amatorszczyzny a nie profesjonalizmu. Dobry programista ma myśleć i pisać z dobrze i z wyobraźnią a nie wg szablonu. Wg szablonu to pracuje się "od 6 rano na taśmie w fabryce azbestu" :-)

Przykłady:

/* This is Hugo Krawczyk's HKDF:
 *  - https://eprint.iacr.org/2010/264.pdf
 *  - https://tools.ietf.org/html/rfc5869
 */
static void kdf(u8 *first_dst, u8 *second_dst, u8 *third_dst, const u8 *data,
		size_t first_len, size_t second_len, size_t third_len,
		size_t data_len, const u8 chaining_key[NOISE_HASH_LEN])
{
	u8 output[BLAKE2S_HASH_SIZE + 1];
	u8 secret[BLAKE2S_HASH_SIZE];

	WARN_ON(IS_ENABLED(DEBUG) &&
		(first_len > BLAKE2S_HASH_SIZE ||
		 second_len > BLAKE2S_HASH_SIZE ||
		 third_len > BLAKE2S_HASH_SIZE ||
		 ((second_len || second_dst || third_len || third_dst) &&
		  (!first_len || !first_dst)) ||
		 ((third_len || third_dst) && (!second_len || !second_dst))));

	/* Extract entropy from data into secret */
	blake2s256_hmac(secret, data, chaining_key, data_len, NOISE_HASH_LEN);

	if (!first_dst || !first_len)
		goto out;

	/* Expand first key: key = secret, data = 0x1 */
	output[0] = 1;
	blake2s256_hmac(output, output, secret, 1, BLAKE2S_HASH_SIZE);
	memcpy(first_dst, output, first_len);

	if (!second_dst || !second_len)
		goto out;

	/* Expand second key: key = secret, data = first-key || 0x2 */
	output[BLAKE2S_HASH_SIZE] = 2;
	blake2s256_hmac(output, output, secret, BLAKE2S_HASH_SIZE + 1,
			BLAKE2S_HASH_SIZE);
	memcpy(second_dst, output, second_len);

	if (!third_dst || !third_len)
		goto out;

	/* Expand third key: key = secret, data = second-key || 0x3 */
	output[BLAKE2S_HASH_SIZE] = 3;
	blake2s256_hmac(output, output, secret, BLAKE2S_HASH_SIZE + 1,
			BLAKE2S_HASH_SIZE);
	memcpy(third_dst, output, third_len);

out:
	/* Clear sensitive data from stack */
	memzero_explicit(secret, BLAKE2S_HASH_SIZE);
	memzero_explicit(output, BLAKE2S_HASH_SIZE + 1);
}

2
katakrowa napisał(a):

Bezmyślne stosowanie nawet najlepszych wzorców tylko dlatego, że są popularne to przejaw amatorszczyzny a nie profesjonalizmu.

Ja bym w ogóle powiedział o bezmyślnym stosowaniu wzorców. Tak samo jest z dogmatycznym podejściem, poradami w stylu "zmień język" czy frazesami jak "mocki to zło".

6

IMO należy rozróżnić szeroko rozumiany "kod biznesowy" (np. wysyłanie maili z powiadomieniami o nowych produktach) od kodu implementującego algorytm kryptograficzny / działającego w kernelu itd.; na ogół rozmowy o dobrych praktykach pojawiają się w kontekście systemów ERP-podobnych, a nie modułów kernela.

na dzień dobry używają Go to

W przypadku C, z braku obsługi scoped-based destruktorów czy liniowych typów [^1], niejednokrotnie inne opcje nie mają sensu.

<sarkazm>gdyby, poprawia muszkę, napisali ten fragment w Ruście, to obeszłoby się bez goto - a tak to amatorszczyzna sieje spustoszenie w kernelu.</>

Dobry programista ma myśleć i pisać z dobrze i z wyobraźnią a nie wg szablonu.

Nikt nie twierdzi inaczej; na ogół rozmowy o dobrych oraz złych praktykach przeprowadzane są w tonie dobieraj narzędzia do problemu, a nie jak zrobisz metodę w DTO to przyjdzie CTO i zrobi Ci BSOD w brain, więc pamiętaj: DTO może mieć tylko pola będące VO i zero metod.

Tymczasem sam Pan Linus Torvalds powiedział, że ten kod to "dzieło sztuki"

[...] compared to OpenVPN and IPsec ;-)

[^1] linear type system - nie jestem pewien czy i jak wygląda tłumaczenie na polski

4

@katakrowa moim zdaniem w ogóle nie rozumiesz kontekstu. Jak często ktoś będzie czytał/zmieniał ten kod? Przypuszczam, ze nigdy. "Dobre praktyki" są po to, żeby kod dało się czytać/rozszerzać. Co więcej bardzo możliwe że ten kod początkowo był "ładny", ale jak juz był gotowy to sie go "zoptymalizowało" (np. inlinując kawałki kodu i zostawiając komentarz zamiast wywołania funkcji, tak zeby nie polegać na kompilatorze który albo coś zoptymalizuje albo nie)

0

@Patryk27: IMO należy rozróżnić szeroko rozumiany "kod biznesowy" no popatrz teraz już trzeba rozróżnić? Jeszcze kilka dni temu nie było takiej potrzeby bo przecież kompilatory rewelacyjnie inlajnują funkcje ...

0

Jeszcze kilka dni temu nie było takiej potrzeby bo przecież kompilatory rewelacyjnie inlajnują funkcje ...

Niestety nie rozumiem do którego tematu się odnosisz - mógłbyś podesłać linka?

0

Dla wyjaśnienia - absolutnie nie jestem zwolennikiem długich funkcji w szczególności od momentu jak przyszło mi debugować, zrozumieć i poprawiać tą z załącznika :-)
Chodzi o to, że często popadamy w ideologiczne skrajności a to niemal zawsze na dłuższą metę nie przynosi nic dobrego.
Powyższy temat wrzuciłem jako ciekawostkę popartą opinią raczej niekwestionowanego w świecie programowania autorytetu.
Oczywiście pewnie znajdzie się ktoś kto napisze, że Linus to się nie zna bo od młodości tylko grzebał sobie w jądrach zamiast zająć się uczciwą praca ... jednak kto ma być tym autorytetem jeśli nie on?

2
katakrowa napisał(a):

jednak kto ma być tym autorytetem jeśli nie on?

http://www.ece.ubc.ca/~sasha/papers/eurosys16-final29.pdf

1

Time: 22:00
https://gist.github.com/dukeofgaming/2150263

So, an example of actual distribution is, you have a group of five people working on one small particular feature. And that means that for a while that feature will be very very broken, right? Because nobody actually creates perfect code the first time around, except me, but there is only one of me, right? So what happens is they want/need to have their own tree, that they can work in, without affecting other people. You can do this in many different ways.

1

@katakrowa: jeśli uważasz że funkcje na 50 linijek są długie to chyba nie pracujesz jako programista.
Akurat ten kod z pierwszego postu mi się mieści na jednym ekranie, więc jedyne co może tu stanowić czelendż to domena - działka security to nie jest coś do zdebugowania przed "daily".
Poza tym jest tam wywoływana tylko jedna funkcja niestandardowa (blake2s256_hmac).
GOTO tutaj ma tyle znaczenia co spacja w pętli for przed lub po średniku.

2

ekstrema considered harmful

2

Frank Rubin wiecznie żywy.

3

Ten kod jest napisany w C, w każdym języku trzeba do tego podchodzić inaczej. Problemy które tu widzę to brak automatycznego zwalniania zasobów (czyszczenie stosu), dużo kopiuj-wklej i ciągłe mutowanie. Pisząc taki kod nie możemy polegać na local reasoning, bo trzeba mieć całą funkcję w głowie. Dodatkowo analizując ten kod trzeba stworzyć mentalny model tego co tu się dzieje w głowie np. kolejne rundy wywołania algorytmu to copy pasta, którą trzeba połączyć w głowie, żeby zrozumieć co się dzieje. W takim kodzie nie chciałbym dużej ilości małych funkcji, bo w sytuacji, gdzie każda linia może ugryźć chcę wiedzieć co się dzieje. Zupełnie inaczej to wygląda w językach wysokiego poziomu, gdzie np. dzieki niemutowaniu można analizować kod kawałek po kawałku bez strachu, że coś wybuchnie

1 użytkowników online, w tym zalogowanych: 0, gości: 1