Jak przestać projektować i zacząć projektować ten projekt zgodnie z sugestią mojego kierownika? [Zamknięte]


42

Jestem młodszym programistą (ok. 3 lata) i przy mojej pracy opracowujemy nowy system. Mój główny programista będzie głównym architektem, jednak rzucił mi wyzwanie, abym sam spróbował zaprojektować system (równolegle).

W trakcie kilku iteracji pomysłów na burzę mózgów i proponowania tego, co widziałem jako sugestie dotyczące architektury, mój trop dał mi informacje zwrotne, że większość tego, co robiłem, to „projektowanie”, a nie „architektura”.

Opisał różnicę jako architekturę niezależną od implementacji, podczas gdy projekt jest opisem implementacji. Powiedział, że muszę zdjąć kapelusz projektanta i założyć kapelusz architekta. Dał mi trochę rad, jak to zrobić, ale chciałbym również zapytać:

Jak wyjść z trybu projektanta oprogramowania i zacząć myśleć bardziej jak architekt?


Oto kilka przykładów „projektów”, które wymyśliłem i które nie były postrzegane przeze mnie jako związane z architekturą:

  1. Wymyśliłem algorytm do ładowania i rozładowywania zasobów z naszego systemu, a mój szef powiedział, że algorytmy kategorycznie nie są architekturą.
  2. Wymyśliłem zestaw zdarzeń, które system powinien wywoływać i w jakiej kolejności powinien je wywoływać, ale to również nie wydawało się ograniczać go jako architektury.

Wydaje mi się, że wpadam w szczegóły i nie cofam się wystarczająco daleko. Uważam, że nawet kiedy wpadam na coś, co jest na poziomie architektury, często do tego dochodzę, wypróbowując różne implementacje i zagłębiając się w szczegóły, a następnie uogólniając i abstrahując. Kiedy opisałem to mojemu szefowi, powiedział, że podchodzę do niewłaściwego podejścia: musiałem myśleć „od góry do dołu”, a nie „od dołu do góry”.


Oto kilka bardziej szczegółowych informacji o projekcie :

  • Projektowany przez nas projekt to aplikacja internetowa.
  • Szacuję około 10-100 tysięcy wierszy kodu.
  • Jesteśmy start-upem. Nasz zespół inżynierów liczy około 3-5 osób.
  • Najbliższą rzeczą, z którą mogłem porównać naszą aplikację, jest lekki CMS. Ma podobną złożoność i zajmuje się głównie ładowaniem i rozładowywaniem komponentów, zarządzaniem układem i modułami w stylu wtyczek.
  • Aplikacja jest ajax-y. Użytkownik raz pobiera klienta, a następnie żąda danych, tak jak potrzebuje tego serwer.
  • Będziemy używać wzoru MVC.
  • Aplikacja będzie miała uwierzytelnianie.
  • Nie jesteśmy bardzo zaniepokojeni starą obsługą przeglądarek (whew!), Więc chcemy wykorzystać najnowsze i najlepsze, które są i będą dostępne. (HTML5, CSS3, WebGL ?, Rozszerzenia źródeł multimediów i wiele więcej!)

Oto niektóre cele projektu :

  • Aplikacja musi być skalowana. W najbliższym czasie nasi użytkownicy będą rzędu od setek do tysięcy, ale planujemy od dziesiątek tysięcy do milionów i więcej.
  • Mamy nadzieję, że aplikacja będzie dostępna na zawsze. To nie jest rozwiązanie tymczasowe. (W rzeczywistości mamy już rozwiązanie tymczasowe, a my projektujemy długoterminowy zamiennik tego, co mamy).
  • Aplikacja powinna być bezpieczna, ponieważ może mieć kontakt z wrażliwymi danymi osobowymi.
  • Aplikacja musi być stabilna. (Idealnie byłby stabilny na poziomie Gmaila, ale nie musi znajdować się na skraju łazika Marsa).


78
Architekt nie nosi kapelusza, a raczej przewiduje abstrakcyjny system ochrony głowy.
Jon Raynor,

3
Czy możesz podzielić się tym, co wymyśliłeś? Waham się przed opisaniem architektury jako agnostycznej w realizacji ... diabeł zawsze tkwi w szczegółach. To powiedziawszy, nie chcesz, aby szczegóły przesłaniały duży obraz. Trudno powiedzieć, co się dzieje bez dodatkowych informacji.
Telastyn

4
Nie czuj się źle, po 3 latach nie spodziewałbym się, że będziesz w stanie wykonać abstrakcyjne skoki, do których on cię popycha. Przypuszczam, że to robi, ponieważ lubi twoją pracę i stara się pomóc ci w opiece mentorskiej, dając ci zadanie poza twoim zasięgiem, aby pomóc ci się rozwijać i uczyć. Jeśli naprawdę chce, abyś odniósł sukces w tym zadaniu do tego stopnia, że ​​ma udaną architekturę, to myli się z ilością doświadczenia potrzebnego, aby ktoś przyzwyczaił się do dostrzegania wzorów, które są na tyle ogólne, że można je postrzegać jako podejście architektoniczne.
Jimmy Hoffa

3
@Daryl: Z pewnością uważam, że warto się uczyć, chociaż skontaktowałbym się z twoim architektem i dowiedziałem się, jakich diagramów faktycznie używa (niektóre UML mają wątpliwą wartość).
Robert Harvey

Odpowiedzi:


26

Przede wszystkim powiedziałbym, że różnica między architekturą a designem to głównie semantyka. Niektóre drużyny mają punkty kontrolne między nimi. Twój przewodnik techniczny definiuje architekturę tak jak przed projektowaniem i architekturę jako agnostyk implementacyjny. Zakładam, że mówimy o projektowaniu jak w modelu kaskadowym, a nie o wzornictwie przemysłowym, które pomogłyby Ci zaprojektować produkt z myślą o użytkownikach, zanim przejdziesz do architektury oprogramowania. Wydaje mi się, że architektura często wsuwa się w projektowanie i nie jest to wcale złą rzeczą, architekt często ma głęboką wiedzę na temat tego, co jest możliwe w danym systemie.

Powiedziawszy to wszystko, potrzebujesz porady na temat obecnej sytuacji. Istnieje cały świat architektury oprogramowania, dokumenty, książki, konferencje, ale ogólnie szukasz wzorów i abstrakcji. Bez dalszych szczegółów na temat projektu mogę jedynie podać szeroki przykład. Na przykład, jeśli pracujesz nad integracją, istnieje architektura zorientowana na usługi ( SOA)), w którym dzielisz części systemu na „usługi”, abyś mógł pracować z każdą częścią w określony sposób, w programie internetowym jest to często wdrażane jako usługi sieciowe (choć nie powinno to być tak ograniczone) a ostatnio wzrost liczby interfejsów API RESTful z JSON, znowu powiedziałbym, że jest to projekt pochodzący z architektury SOA. Powiedziałbym, że Model, widok, kontroler (MVC) to kolejny przykład powszechnie stosowanego wzorca architektury, który dzieli odpowiedzialność komponentów systemu, umożliwiając zamianę części, zawieranie błędów i testowanie.

Z poziomu 10 000 stóp, jeśli możesz narysować go na tablicy i wyjaśnić kompetentnemu programistowi, który nie pracuje w twojej dziedzinie i nie zna twojego języka programowania oraz bieżących szczegółów implementacyjnych, to prawdopodobnie jest to architektura. Jeśli potrafisz napisać o tym książkę, na którą zainteresowałby się ktoś spoza twojej firmy, to prawdopodobnie jest to architektura. Jeśli znajdziesz swój własny objaśniający szczegół i nie możesz go uogólnić na inne bazy kodu / firmy / branże, to prawdopodobnie jest to projekt.

Zgadzam się, że dwa przykłady, które podajesz, to projektowanie kodu, a nie architektura. Po pierwsze, ponieważ myślę, że kiedy powiedziałeś, że wymyśliłeś „algorytm” do ładowania zasobów, myślę, że masz na myśli, że zaprojektowałeś zestaw instrukcji do wykonania tego zadania, a nie że zaprojektowałeś nowy algorytm, którego będą uczyć w pierwszym roku COMSC w przyszłym roku. W drugim przykładzie ponownie zgadzam się, że to projekt. Jeśli pokazałeś mi któryś z tych pomysłów, nie byłbym w stanie wykorzystać ich w moim losowym projekcie oprogramowania. Musisz przejść do „wyższego poziomu”, obiektowego (OO) w Javie, a nie chcę, aby klasa klienta była podklasą klasy osoby. Nawet mówienie o wyjątkach w ogóle można uznać za zbyt niski poziom (zbyt blisko wdrożenia).

Aby spróbować zająć się specyfiką, którą wymieniasz, myślę, że powinieneś pomyśleć o tym, jak zaprojektować internetowy CMS. Wordpress ma kodeks architektury witryny, w którym dużo mówią o szczegółach implementacji projektu, ale z postu jasno wynika, że ​​ich główna architektura skupia się na tym, aby Wordpress był rozszerzalny o motywy. Zaprojektowanie przejrzystego interfejsu dla tematu, który mógłby zostać napisany przez kogoś spoza firmy, było wyraźnie decyzją podjętą przez architekturę. Tego rodzaju rzeczy warto wziąć na papier, projektując swoje „długoterminowe” (nie tymczasowe) rozwiązanie, aby wszystkie decyzje projektowe i wdrożeniowe były podejmowane podczas programowania (przez wszystkich programistów, nie tylko architekta) są zgodne z tym pomysłem.

Inne przykłady architektury dla twojej sytuacji:

  1. Przeniesienie całości na maszyny wirtualne, hostowane w chmurze lub w domu i posiadanie bezstanowych wystąpień maszyny, aby każda awaria maszyny mogła zostać zastąpiona nową instancją maszyny wirtualnej bez konieczności kopiowania w dowolnym stanie lub utraty jakichkolwiek informacji.
  2. Od samego początku budowanie w testach niepowodzenia produkcji na żywo z małpami chaosu .

Może spróbuj narysować cały system na tablicy. Wypróbuj na różnych poziomach szczegółowości, pierwszą płytą może być GUI-> dyspozytor-> backend-> DB lub coś, a następnie drążenie w dół, aż zaczniesz używać zaimków.


Dodałem trochę więcej szczegółów do rodzaju projektu, nad którym pracuję. (PS Dziękuję za odpowiedź! Przetwarzam tam wiele pomocnych szczegółów.)
Daryl

2
Oznaczenia takie jak „Aktualizuj adres edycji OP” są niepotrzebne. Pełna historia zmian jest zachowywana dla każdego postu, w tym także tego , i możesz określić „powód edycji” w polu Podsumowanie edycji na stronie edycji .
Robert Harvey

1
„często bardzo pomocny dla architekta, który ma głęboką wiedzę o tym, co jest możliwe”. Myślę, że to najważniejsze. Nie wyobrażam sobie życia w budynku, w którym architekt nie miał wiedzy na temat możliwości drewna, betonu i szkła. Im głębsza wiedza, tym bardziej ekscytująca i przełomowa architektura.
Chris Wesseling

Chociaż prawie wszystkie odpowiedzi tutaj były pomocne, twoje prawdopodobnie były najbardziej pomocne, a społeczność wydaje się również najbardziej przydatna.
Daryl

16

Rozróżnienie między tymi dwoma pomysłami jest naprawdę ważne w mojej pracy.

To, co nazywacie „architekturą”, nazywamy „programowaniem w języku angielskim”. Jest to częściowo ważne, ponieważ jeśli nie możesz opisać tego nieprogramiście, coś jest nie tak. Może być tak, że nie rozumiesz problemu wystarczająco dobrze, LUB może być to, że rozwiązujesz problem „fantomowy” (nie omawiany tutaj).

Terminy użyte w tych dwóch różnych aspektach projektowania są często różne, ale zasady są łatwo rozpoznawalne wszędzie. Jeden aspekt (w twoim przypadku architekt, w moim przypadku projektant) programuje w języku angielskim, podczas gdy drugi (w twoim przypadku „projektant”, w moim przypadku „programista”) programuje w określonym języku. Są również dość powszechnie rozróżniane jako „projekt” i „implementacja”. „Projekt” jest tym, co powinien osiągnąć, a „implementacja” jest kodem, który to umożliwia.

Kilka przykładów z tego, nad czym pracowałem:

Architektura jednego programu jest następująca: potrzebujemy scentralizowanego menedżera lub koncentratora, do którego możemy łatwo dodawać moduły. Ten menedżer będzie dystrybuował zdarzenia do wszystkich zarejestrowanych modułów. Moduł może zarejestrować się w Menedżerze zdarzeń, a tym samym publikować zdarzenia w pozostałej części systemu i odbierać zdarzenia, na których mu zależy. Każdy moduł ma „skrzynkę pocztową”, którą może sprawdzić i opróżnić według własnego uznania. Pozwoliłoby nam to pomieścić nowe moduły, których jeszcze nie potrzebujemy.

Brak kodu. Może być napisany w dowolnym języku. Wdrożenie nie jest podyktowane tym opisem.

Architektura innego projektu jest następująca: potrzebujemy sposobu, aby niezawodnie uruchamiać i zatrzymywać inne programy bez czekania na nie. Możemy mieć menedżera odpowiedzialnego za konkretny program. Możemy po prostu nakazać mu uruchomienie lub zatrzymanie programu, a menedżer się tym zajmie. Jeśli ten inny program zostanie poproszony o zatrzymanie i nie zrobi tego w określonym czasie, menedżer wie, jak zmusić go do zatrzymania i posprzątać bałagan. Program nie jest uruchamiany ani zatrzymywany przez nic innego, a menedżer może zostać zapytany, czy jego program jest uruchomiony, zatrzymany lub czeka na zatrzymanie. To pozwala nam kontynuować inne rzeczy, które musimy zrobić, jednocześnie uruchamiając i zatrzymując te programy poprawnie.

Ponownie nic tutaj nie nakazuje implementacji, chociaż niektóre implementacje są wyraźnie bardziej przydatne niż inne.

Różnica między projektowaniem (co nazywamy wzorami lub implementacją) a architekturą (co nazywamy projektem) polega na tym, że jeden z nich rozwiązuje problem kodowania / implementacji, a drugi rozwiązuje problem w świecie rzeczywistym.


2
Twoje rozróżnienie na język naturalny jest interesujące i bardzo pomocne dla mojego celu. Dzięki!
Daryl

14

Może to pomoże. Zawsze postrzegałem starszeństwo inżyniera jako pytanie, jak duży problem mogą rozwiązać samodzielnie.

Z grubsza, aby przekazać pomysł:

  • Możesz dać komuś nowemu programowanie małych, zamkniętych zadań z mnóstwem wyraźnych instrukcji na temat tego, jak zadanie musi zostać zintegrowane z innymi elementami

  • Deweloper średniego poziomu to ktoś, kto może wziąć opis pewnej części aplikacji i sprawić, by działała w kontekście tej aplikacji.

  • Starszy programista może od podstaw zbudować aplikację średniej wielkości, w ramach technicznych ograniczeń sklepu.

  • Bardziej zaawansowany programista może to zrobić i dokonać wyboru technologii na temat tego, jakich technologii użyć, aby działała dobrze.

... ale to nie są twarde i szybkie zasady. I niektórzy ludzie wychodzą z bramy jako „starszy” IMHO, nawet jeśli muszą spędzić trochę czasu z innym tytułem.

Architekt prosi o spojrzenie na problem jeszcze bardziej ogólnie. Jeśli musiałbyś połączyć kilka aplikacji, aby system działał:

  • Jakie aplikacje i usługi będą potrzebne?
  • Jakie elementy wchodzą w interakcje z klientami, a które ze sobą?
  • Jak się komunikują?
  • Gdzie będą przechowywać dane?
  • Gdzie jest ryzyko awarii?
  • Jak zapewnisz niezawodność?
  • Jak zapewnisz bezpieczeństwo?

W pewnym sensie architektura techniczna przypomina architekturę budowlaną. To układ lub plan. Pokazuje, co jest potrzebne dla różnych części, jak się trzymają razem i co równie ważne dlaczego .

(BTW, mam podobną krzywą wzrostu wyjaśnioną dla architektów, od architektury rodziny powiązanych aplikacji lub zestawu bardzo złożonych funkcji, po ustalenie kierunku technicznego dla grupy, po podejmowanie strategicznych decyzji technicznych dla całej organizacji .)

To powiedziawszy, myślę, że większość inżynierów na wszystkich poziomach musi również wykonać „architekturę”. To nie jest jasna linia. Wygląda na to, że jeśli najpierw skoncentrujesz się na dużym obrazie i nie rozwiesz się ze szczegółami implementacji, będziesz bardziej zgodny z tym, czego szuka. BTW, możliwość zobaczenia zarówno Wielkiego Obrazu, jak i Małych Szczegółów, jest ogromnym atutem w tej branży, więc brzmi to jak świetna okazja.

... Oto analogia. Powiedzmy, że zostałeś poproszony o stworzenie Magic Black Box. Jako inżynier masz obsesję na punkcie wewnętrznych działań Magicznej Czarnej Skrzynki. Ale jako architekt koncentrujesz się na czymś innym. Możesz zajrzeć do pudełka z ciekawości, ale oczekuje się, że będziesz mieć obsesję na punkcie wszystkiego wokół wszystkich Magicznych Czarnych Skrzynek.

Podobnie do tej analogii, możesz pomyśleć o roli architektury jako postrzeganiu całego systemu jako magicznej skrzynki. Więc jeśli weźmiesz gigantyczny szklany box i umieścisz klientów, aplikacje klienckie, zaporę ogniową, warstwę usług, bazę danych, a nawet facetów devops w środku, to jako architekt masz obsesję na punkcie tego, jak zrobić to ogromne pudełko systemowe działa dobrze .


2

Dokładna różnica między „projektowaniem” a „architekturą” jest nieco subiektywna i zachodzi pewne nakładanie się. Jest to jednak różnica, którą rozumiem:

Architektura patrzy na koncepcje wysokiego poziomu. Kim są aktorzy w systemie? Jakie są główne przedmioty i za które są odpowiedzialne? Kiedy myślę o architekturze, myślę, że Visio, a nie kod.

Na przykład system zdarzeń może mieć menedżera zdarzeń, który przyjmuje zdarzenia przychodzące i wysyła je do procedur obsługi zdarzeń. Idea wątków, asynchroniczne i synchroniczne oraz inne koncepcje niższego poziomu nie wchodzą tutaj w grę. MVC jest kolejnym przykładem: konkretne szczegóły nie są określone na wysokim poziomie interakcji trzech elementów, tyle że istnieją trzy osobne problemy obsługiwane przez osobne pakiety kodu.

Projektowanie obejmuje prototypowanie, szkicowanie interfejsów kodu, szkieletów kodu itp. Znajduje się między abstrakcyjną architekturą a niskopoziomową pracą „małpy kodowej”.

W strukturze zdarzeń projekt może powiedzieć „zdarzenie korzysta z tego interfejsu” i „istnieje pula wątków, której menedżer zdarzeń używa do wysyłania zdarzeń do pracowników”. Projekt dla MVC może być „użyj Hibernacji dla modelu, sprężyny dla kontrolera i GWT dla widoku”.

Kiedy wykonuję prace projektowe, szkicuję interfejsy i szkielety kodu, a następnie przekazuję wyniki programistom. Czasami jestem tym programistą. Ale są to dwie odrębne fazy i obie istnieją bardziej w kierunku betonu niż architektury.

Nałożenie kapelusza architektury oznacza oczyszczenie kodu i myślenie o obiektach na tablicy. Pomyśl o obiektach, pakietach, ramach i przepływie komunikatów między nimi. Jeśli myślisz o choć jednym wierszu kodu, robisz to źle. Nie daj się wciągnąć w coś takiego: „och, ta wiadomość może być ciągiem znaków lub użyć SOAP, lub cokolwiek innego”. Na tym poziomie wystarczy fakt, że komunikacja ma miejsce. Szczegóły są nieistotne.


2

Jeśli mogę coś tutaj dodać , to tak: nie myśl kodu . W ogóle.

Nie myśl, jak napiszesz kod, aby coś osiągnąć, ale pomyśl, jaka byłaby najlepsza metoda osiągnięcia tego celu. Twój opis tego, co musisz zrobić, powinien być niezależny od języka , dlatego będziesz omawiać wzorce projektowe - które są wspólnym „językiem” między użytkownikami różnych języków programowania - w celu omówienia dalszego postępowania.

W przypadku konkretnego przypadku użycia, moim zdaniem, więcej pytań architektonicznych byłoby w stylu:

  • Dlaczego korzystasz z MVC? Czy to wszystko, co wiesz? Czy są lepsze wzorce do użycia? Dlaczego?
  • Z jakich ram będziesz korzystać i dlaczego ?
  • Jak skalujesz? Nie kodowo, bo to jeszcze nie ma znaczenia. Jakie będą warunki do skalowania w poziomie; jakiej usługi (AWS) użyjesz do tego?
  • Jak będzie przeprowadzane uwierzytelnianie? Nie kodowo: czy będziesz generował losowy, unikalny token na każde żądanie i sprawdzał go względem oczekiwanego tokena? Nie myśl, jak to zrobisz w kodzie. Zastanów się, dlaczego to robisz - ponieważ można to zrobić w praktycznie dowolnym języku internetowym.

Zasadniczo nie mów wcale o kodzie. Nawet jeśli nie wiesz, jak coś zrobić, kiedy jest wola, istnieje sposób . Zastanów się, jak najlepiej pasują do siebie elementy układanki, zanim zaczniesz martwić się ich złożeniem.


1

Pomyśl o wszystkich operacjach (tj. Przypadkach użycia), które może wykonać Twój system. Dla każdej operacji udokumentuj, co dzieje się w Twojej domenie biznesowej dla każdej operacji. Powinieneś rozmawiać tylko w kategoriach problematycznych domen i nie opisywać żadnych szczegółów implementacji. Narysuj duży blok i nazwij go Systemem. Połączenie tego „dużego bloku” i opisów operacji stanowi architekturę systemu najwyższego poziomu.

Chociaż ta architektura wysokiego poziomu zapewnia przyzwoity punkt wyjścia, tak naprawdę nie ma dużej wartości podczas budowania rzeczywistego systemu. Musisz obniżyć poziom szczegółowości, aby przekształcić go w użyteczną architekturę.

Podążasz za tym samym ogólnym pomysłem, co podejście „dużego bloku”, tylko zaczynasz dodawać „podbloki”, które są niezbędne do wykonania każdej operacji. Te „podbloki” są często nazywane klasami domen lub modułami. Można je łatwo zidentyfikować za pomocą opisów operacji (z podejścia dużego bloku) i rysowania schematów sekwencji. Są one nazywane klasami domen, ponieważ nie są przeznaczone do „prawdziwych” klas, ale mają na celu opisanie twojego systemu w kategoriach problematycznej domeny twojego systemu.

Efektem końcowym utworzenia całego diagramu sekwencji i zidentyfikowania wszystkich „podbloków” jest to, że masz teraz listę klas domen i ich listy operacji. Zwykle kończy się to dość użyteczną architekturą oprogramowania, w której każdy z „podbloków” / modułów może zostać wysłany do różnych programistów w celu zaprojektowania i wdrożenia.

Oczywiście, jeśli pozostawisz to bez zmian, Twoi projektanci będą mieli ze sobą wiele interakcji podczas definiowania interfejsów między modułami. Architekt może więc decydować o konkretnych mechanizmach interfejsu i typach danych, które będą używane między modułami.

Ponadto niektóre „podbloki” będą nadal bardzo skomplikowane pod maską. Dlatego może być konieczna kolejna iteracja podejścia „podblokowego”, ale tylko tym razem na jednym z nowo zidentyfikowanych modułów.

Wreszcie mogą istnieć pewne specyficzne wzorce / ograniczenia między interfejsami, których architekt chce, aby system przestrzegał (np. Wywołania zwrotne zdarzeń w porównaniu z bezpośrednimi wywołaniami metod), więc o tych decyzjach trzeba będzie mówić w projekcie architektonicznym. Dodatkowo mogą istnieć „wspólne” moduły, których architekt chce, aby wszyscy używali.


0

Jako programista prawdopodobnie jesteś przyzwyczajony do dostarczania rozwiązań. To bardzo dobry sposób myślenia, ale może utrudniać myślenie o architekturze.

Uważam, że pomaga opisać problem, który próbujesz rozwiązać w pierwszej kolejności. Jakie są wymagania? Jakie są ograniczenia? Czy możesz porozmawiać z interesariuszami, aby dowiedzieć się o tych wymaganiach?

Może to pomóc, jeśli możesz również opisać swoje własne cele projektowe. Czy Twoje rozwiązanie wymaga skalowania, czy wystarczy projekt dla pojedynczego użytkownika? Jak długo twoje rozwiązanie musi pozostać ważne? Czy jest to jednorazowa poprawka czy długoterminowe rozwiązanie infrastrukturalne? Być może równie ważne: jakie są granice twojej architektury?

A ponieważ jest to doświadczenie edukacyjne, nie bój się zadawać pytań, nawet jeśli są głupie.


1
Dla większej przejrzystości wyliczyłem niektóre cele projektu.
Daryl
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.