All we do is looking for some way to fulfill our needs.

piątek, 9 grudnia 2011

Tagged under: , , ,

Dlaczego Agile nie zadziała

Wprowadzenie metody z rodziny Agile nie jest w ogóle łatwe. Problemem zazwyczaj jest to, że menedżment powierzchownie dowiadując się o co chodzi, odbiera nową metodę jako obietnicę - od teraz w magiczny sposób wszystko zacznie lepiej działać. Nieważne, że mamy kiepskich ludzi i hołdujemy zasadzie "każdego specjalistę można zastąpić przez skończoną liczbę studentów". Nieważne, że kompletnie nie dba się o zarządzanie wiedzą i umiejętności w zespole, bo nigdy nie ma na to czasu. Nieważne, że osoby realizujące projekty są przerzucane między projektami - bo przecież chodzi o interdyscyplinarność i każdy się powinien we wszystkim orientować. Nieważne, że terminy ustala się apriori, z do końca niejasnych przyczyn lub zakłada się, że to jedyny sposób, żeby przekonać klienta, że jesteśmy profesjonalni, a w terminie się "jakoś zmieścimy". Nieważne, że nie znajdujemy czasu na to, aby utrzymywać architekturę i kod, bo przecież nie ma czasu na tego typu zbędne czynności.
Nieważne, bo przecież Agile obiecuje, że teraz będzie już tylko pięknie. Dokleimy etykietkę to tego co robimy teraz - niech się to nazywa np. "Scrum", zrobimy trochę więcej chaosu, bo przecież chodzi o zarządzanie chaosem - zatem każdy robi co chce i jak chce, przestajemy cokolwiek dokumentować. Przecież stosujemy Agile, wszystko się samozorganizuje.

I dlatego najczęściej Agile nie działa, bo ludzie, którzy po omacku go stosują lub menedżment, który wstępnie akceptuje korzyści z danego podejścia, stosują bardzo uproszczone myślenie - "Ok. Robimy Scrum. Ale tak naprawdę 8 z 10 elementów nam nie pasuje, więc wybierzemy sobie, to co pasuje (dwa elementy), a resztę będziemy robić tak jak do tej pory (czytaj wyżej).
I tak wszystko będzie tak jak było, albo gorzej.

ZATEM UWAGA: W takim przypadku nie należny, nawet nie WOLNO zacząć stosować Agile. Agile to zmiana, ogromna zmiana, to nie etykietka, która rozwiąże wszystkie problemy (w tym przypadku prawdopodobnie lepiej się sprawdzi butelka alkoholu).

sobota, 26 listopada 2011

Tagged under: , , ,

Efektywne poznawanie tematu

Tym razem temat nieco wychodzący poza projekty informatyczne. Ostatnio dużo robię tzw. pracy researchowej, gdzie muszę na bazie dużej ilości źródeł wiedzy zbudować spójny ekstrakt praktycznych informacji. Po dłuższej pracy w tym temacie napiszę o kilku wskazówkach, które miałem okazję wielokrotnie wykorzystywać.

Jeśli chcesz zbudować wiedzę na pewien temat np. continuous integration, technologia X.
Cel: chcesz naprawdę dobrze zrozumieć temat.
Założenie: Nie znasz nikogo, kto mógłby Cie naprowadzić na dobrą drogę ;-)


1. Przeprowadź spike'a* (maks. kilka godzin) odnośnie źródeł informacji - gdzie co możesz znaleźć i jaki jest poziom materiałów (wiarygodność i szczegółowość):
a) sprecyzuj pytania, na które chciałbyś znaleźć odpowiedzi;
b) w pierwszej kolejności wybieraj książki - jeśli są dobre, znajdziesz tam wiarygodne i uporządkowane informacje;
c) w drugiej kolejności wybieraj materiały akademickie (prace doktorskie, prace z konferencji) - zaletą jest wiarygodność, czasem jednak są nieuporządkowane, czasami (jeśli autor jest kiepski) jest to zwykłe bicie piany;
c) blogi i artykuły wiarygodnych osób w branży (rozpoznawalnych i uznanych);
d) unikaj w miarę możliwości forów, blogów i luźnych informacji, których źródeł (osób) nie kojarzysz - dużo się naczytasz, niewiele dowiesz.
2. Kiedy już określisz listę wiarygodnych źródeł - określ minimum, które jest Ci potrzebne.
3. Jeśli zaczynasz czytać tekst (i dotyczy on Twojego minimum), czytaj go bardzo dokładnie, nie idź do przodu, jeśli nie rozumiesz, tego co przeczytałeś.
4. Wielokrotnie się przekonałem, że lepiej dowiedzieć się mniej, ale mieć wiedzę jak brzytwa, niż przeczytać "o wszystkim" po łebkach.
5. Rób notatki! To jedna z najważniejszych składowych! Wiele osób czyta dużo, ale nie robi notatek. Notatki zmuszają Cię do dokładniejszego zrozumienia tego co czytasz. Żeby zrobić notatkę (zazwyczaj) musisz coś zrozumieć. Niech notatka będzie parafrazą (czyli Twoim własnym sformułowaniem), nie cytatem.

*Spike - ograniczony czasowo, krótki okres na zbadanie tematu (maks. 2 dni), który jest Ci obcy lub mało znany.

czwartek, 13 października 2011

Tagged under:

Trzeba zrobić miejsce...

Z tomiku ... Poezja dla zaangażowanych w projekty

Trzeba zrobić miejsce

Nigdy nie ma dobrego czasu na pierwsze dziecko,
Nigdy nie ma dobrego czasu na drugie dziecko,
Nigdy nie ma dobrego czasu na budowanie domu,
Nigdy nie ma dobrego czasu na remont,
Nigdy nie ma dobrego czasu na ślub,
Nigdy nie ma dobrego czasu, żeby wprowadzić refaktoryzację,
I nigdy nie ma dobrego czasu, żeby zacząć pisać testy jednostkowe,
W każdym z tych przypadków, trzeba po prostu podjąć decyzję,
Zacząć to robić, a czas i środki się znajdą,
Bo będą musiały...

poniedziałek, 10 października 2011

Tagged under: , ,

Klienci chcą być zwinni...

Jedno z ostatnich spotkań, gdzie ustalam (MS) sposób współpracy z klientem (szczegóły celowo nieco zmodyfikowane lub usuniętę, jednak bazujące na rzeczywistym przypadku). Klient (K) mówi:
K: To bardzo poważne przedsięwzięcie i zapowiadają się poważne koszty, prawdę mówiąc nasz sponsor ma spore wątpliwości, co to tej sumy. Nie będzie w stanie jej wydać. Projekt jest dość innowacyjny i nie wiadomo, jak odpowie rynek...
MS: To nawet dobrze się składa, ponieważ możemy zaproponować inny sposób współpracy. Cały system to rzeczywiście spora inwestycja i dość ryzykowna, gdyż produkt zwyczajnie może się nie przyjąć. Przyjrzyjmy się co jest esencją systemu... być może to będzie część, która nam podpowie co dalej zrobić.
(kilkadziesiąt minut wspólnej analizy i dyskusji)
MS: Świetnie! Zatem XYZ to jest to, co przyciągnie klientów, wszystko inne jest wtórne. To nazywamy corem biznesowym tego systemu. Zatem zróbmy tak: zakontraktujmy tę pierwszą część, co do pozostałych części przedstawiliśmy zgrubne szacunki, po tych pracach okaże się, na ile są trafne. Ale co najważniejsze po wdrożeniu core przekonamy się, jak rynek reaguje i czy reaguje, zbierzemy informację zwrotną. Wtedy podejmiemy dalsze decyzje.
K: Ok. To wygląda całkiem obiecująco. Zaryzykujemy tylko 1/4 całej początkowej sumy. Ciekawa sprawa, muszę o tym porozmawiać ze sponsorem.
(... klient zarażony, a konkurencja bez szans, bo ofertująca w tradycyjnym modelu - a tymczasem koncept klienta się zmienił :)

niedziela, 25 września 2011

Tagged under: , , ,

Czas na szkolenie z asertywności!

Tak się składa, że w poprzednim poście pisałem o asertywności w kontekście zarządzania czasem, natomiast analizując różne przypadki, z którymi miałem do czynienia, mogę z dużym przekonaniem stwierdzić, że brak asertywności (wliczam w to odpowiedni sposób komunikacji) jest przyczyną (źródłem) dużej części innych problemów w projekcie.
Weźmy za przykład presję. Zazwyczaj presja idzie z tak zwanej góry. Górą może być klient lub jakiś istotny dyrektor lub prezes (jakby mógł być jakiś nieistotny!). Presja ta jest zazwyczaj przenoszona na kolejne poziomy w dół aż dochodzi do zespołu projektowego (w typowym uogólnionym przypadku: klient/dyrektor=>kierownik projektu=>zespół projektowy).
I teraz uwaga powiem coś co może się niektórym nie spodobać, ale

TA PRESJA JEST POTRZEBNA!

Tak. Nie ma co psioczyć, że ktoś każe zrobić pracę w krótszym terminie. Z ekonomicznego punktu widzenia jest to bardzo rozsądne. Zresztą zgodnie z prawem Parkinsona , którego doświadczyłem wielokrotnie osobiście zarówno jako wykonawca jak i odbiorca czy obserwator, krótkie terminy są wskazane.
Nierozsądne jest poddawanie się tej presji. Jeśli masz przekonanie, że dany termin jest nierealny, powiedz to (asertywnie). Przygotuj się dobrze, przygotuj argumenty (pamiętaj o uwzględnieniu perspektywy swojego rozmówcy, a nie tylko Twojej) i przedyskutuj dlaczego to jest nierealne.
Bardzo często się przekonują, że kierownicy (czy inne osoby decyzyjne) są bardzo skłonni zaakceptować inne szacowanie, jeśli tylko jest ono dobrze uzasadnione. Choć również są i tacy, którzy nie są skłonni (ale nie oszukujmy się - jest ich może z 5%, więc jeśli myślisz, że to na pewno dotyczy Ciebie, to jest duże prawdopodobieństwo, że się mylisz!)

Jeśli więc zastanawiasz się, z czego się przeszkolić, to Twój typ powinien zmierzać w stronę asertywności ;-)

piątek, 23 września 2011

Tagged under:

Klucz do zarządzania czasem

Jest kilka tematów, które cały czas pojawiają się w mojej głowie i wraz z kolejnymi odkryciami przechodzę do kolejnego poziomu jego zrozumienia. Tak jest z "zarządzaniem czasem".
Moje obecne doświadczenie jest takie, że możesz doświadczyć owego "zarządzania czasem" na kilku poziomach głębokości:

1) Znajomość technik - tutaj masz do dyspozycji GTD, czy też tzw. klasyczne zarządzanie czasem. Po prostu technicznie jesteś w stanie zapanować nad tym co robisz, być może używasz jakiegoś narzędzia, jakiejś metody, potrafisz porozdzielać i poukładać swoją zadania w kalendarzu lub na liście TODO.

2) Świadomy wybór rzeczy ważnych i rezygnowanie z "szumu zadaniowego" czyli spraw mało istotnych - większość osób robi to co mają do zrobienia, tak jak leci, często robią to z przyzwyczajenia, bezrefleksyjnie. Nawet jeśli świetnie opanowałeś techniki, to niewiele Ci to pomoże. (więcej: http://msieraczkiewicz.blogspot.com/2010/08/hiperoptymalizator-czasu.html)

3) Kluczem do zarządzania czasem jest asertywność i komunikacja - to trzeci (najwyższy) w mojej typologii poziom. Nigdy nie pozbędziesz się wrażenia, ze masz zbyt mało czasu, jeśli nie jesteś odpowiednio asertywny - wobec szefa, znajomych, rodziny, siebie (sic!) i nie potrafisz tego odpowiednio zakomunikować, nie będąc ani agresywnym ani uległym. Tak to lakoniczne stwierdzenie. Ale niech to będzie koan, dla tych bardziej zainteresowanych :)

Czy będzie czwarty poziom? Niewykluczone ;-)

czwartek, 4 sierpnia 2011

Tagged under: ,

Dotrzymywanie terminów za wszelką cenę to błąd!

Terminy są potrzebne. Nie ważne czy pracujesz w Scrumie (Agile) czy w metodyce wodospadopodobnej - jedno jest pewne - bez terminów brak mobilizacji. Mówi o tym tzw. prawo Parkinsona (nie wiem czy ma on coś wspólnego z chorobą wieku starczego).

Problem pojawia się w sytuacji, kiedy widać, że nie uda się dotrzymać terminu. Klasyczny sposób postępowania polega na brutalnym dotrzymaniu terminu za wszelką cenę! I to jest błąd!

Dlaczego? Zadaj sobie trud i poświęć kilka minut, że znaleźć ciemne strony dotrzymywania terminów za wszelką cenę ...

(Czas na znalezienie...Ci co nie lubią myśleć sami, mogą przejść natychmiast to następnego punktu ;-))

Oczywiście roztrząsane dookoła konsekwencje to:
- nadgodziny (szkodliwe dla psychiki i niewydajne - można się spierać),
- dług techniczny.

Jednak konsekwencje są straszniejsze:


Zespoły, które dotrzymują terminów za wszelką cenę nie uczą się!


Tzn. uczą się tego, że można pozwolić sobie na kiepskie praktyki, nieefektywny sposób pracy, brak asertywności, jeśli w okolicy terminu ponagina się czas pracy oraz przymknie (a w zasadzie zamknie) się oczy na jakość.
Od dziś będę nazywać to Stress-Driven Development :-)

Dotrzymywanie terminów utrwala złe praktyki realizowania projektów.


Oczywiście naiwnym byłoby twierdzić, że niedotrzymywanie terminu jest rozwiązaniem. Nie jest! Bo samo w sobie nic nie da! Potrzeba jeszcze narzędzi wyciągania wniosków i wprowadzania ich w życie.


Kiedy musisz stwierdzić, że nie zdążysz na umówiony termin: będziesz musiał o tym teamleada jeśli jesteś programistą, jeśli jesteś teamleadem - kierownika projektu, jeśli kierownikiem - klienta i/lub kierownika/dyrektora wyższego szczebla, jauććć to może boleć. Dlatego najczęściej tego nie robisz, wolisz być nieasertywny lub nagiąć swój czas lub swoje zasady, aby dotrzymać terminu. Ot cały mechanizm.
Jak zaboli (choć istnieją również mniej bolesne/bezbolesne metody) wtedy będziesz miał okazję rzeczywiście się czegoś nauczyć. Wtedy będziesz mógł się rzeczywiście rozwijać, wtedy będziesz miał motywację, aby zmienić proces, zamiast utrwalać jego nieefektywność.

wtorek, 12 lipca 2011

Tagged under: , , ,

O nazewnictwie słów kilka – metody (in progress)

(Uwaga! Artykuł w trakcie rozwoju!)
Może temat wydaje się banalny i wyświechtany, bo któż nie wie, że trzeba tworzyć jednoznaczne, czytelne nazwy. Natomiast ciągle jest obszar bardzo zaniedbany. Ciągle zespoły są dalekie od zrozumienia, że to właśnie od nazewnictwa zależy najwięcej. Żadna refaktoryzacja nie ma takiej mocy, jak zmiana nazwy. To przede wszystkim nazwy, jeśli stosowane odpowiednio, tworzą tzw. samodokumentujący się kod, kreują jednoznaczny język w źródłach systemu, który tworzysz.
Temat opisany wprawdzie w Clean Code, ale nie tak konkretnie jak ja to widzę, nieco po wierzchu i z innej perspektywy.

Dzisiaj kilka słów o anatomii metody (funkcji). Każda metoda ma tzw. sygnaturę czyli nazwę, parametry oraz zwracany typ, kluczowe składowe z punktu widzenia refaktoryzacji, gdyż na czytelność składają się wszystkie trzy elementy – nie tylko nazwa.
Ponadto można powiedzieć, że są dwa rodzaje metod:
• zwracające wynik;
• niezwracające wynik.
Metody zwracające wynik
(Uwaga! Poniższe zasady nie dotyczą tzw. interfejsów typu fluent)
 
double computeSomeResult(int parameter1, double parameter2, int parameter3 …)
 
• nazwa metody zawsze konkretnie i jednoznacznie musi określać, czym dana metoda się zajmuje i musi z niej wynikać, co będzie jej wynikiem;
• sygnaturę metody twórz zawsze w kontekście klasy, w której się znajduje (nie powtarzaj nazwy klasy w nazwie metody);
• tym samym metody zwracające informacje o błędach powstałych podczas jej wykonania są antywzorcem (choć oczywiście jest śladowa liczba przypadków, kiedy taka strategia może być uzasadniona – zazwyczaj gdy pracujemy z kodem odziedziczonym); tej chorobie ulega wiele frameworków;
• wysoce karalne jest traktowanie parametrów wejściowych jako sposób na zwracanie wyniku, co najczęściej dzieje się w przypadku opisanym w punkcie powyżej;
• parametry metody współtworzą znaczenie z nazwą – muszą ją uzupełniać i być z nią zgodne.
Przykłady i komentarze
1)

 
List<Content> retrieveChildren(Content parentContent)  
 

Ok! Nazwa sugeruje, że będą wyszukiwane dzieci parametru i taki też jest zwrócony typ.
Bardziej fluentowe wersje (TBD wyjaśnić co to):

 
List<Content> retrieveChildrenOf(Content parentContent)  
List<Content> childrenOf(Content parentContent)
 
Lub jeśli byłoby to adekwatne do kontekstu, metoda childrenOf mogłaby być składową klasy Content i wtedy jej sygnatura wyglądałaby:
 
class Content {
   List<Content> retrieveChildren() // ..
   // lub bardziej fluentowo
   List<Content> children() // ..
}
 
2)
 
interface ContentProcessorResolver {
   //…
   /* @return zwraca listę treści dostosowaną do danego resolwera */
   public List<? extends Content> resolve(Content content);
   //…
}
 
Zła nazwa – sugerowałaby (w kontekście interfejsu, w którym się znajduje), że wynikiem będzie obiekt klasy ContentProcessor.

TBC…

wtorek, 5 lipca 2011

Tagged under: , , , , ,

Implementacja - Proste wprowadzenie do BDD cz. 3

JBehave, Behaviour-Driven Development (BDD) ciąg dalszy, tym razem już implementujemy... i kilka ciekawych ficzerów narzędzia...

Have fun!

środa, 29 czerwca 2011

Tagged under: , , , , ,

Proste wprowadzenie do BDD cz. 1

Dzisiaj premiera screencasta, a w zasadzie pierwsze koty za płoty - eksperymentalne próby z blogowaniem wideo i taką formą dzielenia się spostrzeżeniami.

Na pierwszy ogień idzie Behaviour-Driven Development i JBehave. Narzędzie pozwalające bardzo poukładać sobie w głowie, jaką ścieżką przejść od wymagań do implementacji. W rolach głównych występują :) User Stories, Testy akceptacyjne, Test-Driven Development i JBehave. Razem będzie 5 filmów, teraz pierwsze dwa!

Oglądaj koniecznie w rozdzielczości 720p i na pełnym ekranie



Tagged under:

Naturalny porządek refaktoryzacji pod lupą cz. 5 Ewolucja architektury


Dalszym krokiem, na dużo wyższym poziomie abstrakcji i wymagającym dogłębnego zrozumienia systemu. Na bazie pojawiających się wzorców, rozwijających się obiektów dziedzinowych po pewnym czasie dostrzegamy konieczność modyfikacji architektury. Z pomocą mogą nam przyjść wzorce architektoniczne lub wprowadzenie innych mechanizmów architektonicznych. Na tego typu przekształcenia może się składać m. in.:
  wprowadzanie warstw;
  wprowadzenie lub zmiana O/RM;
  zmiana organizacji logiki biznesowej;
  wprowadzenie lub zmiana szkieletu aplikacji.
Często w systemach przyjmuje się, że stworzona raz architektura będzie doskonale spełniać swoje zadanie przez cały cykl życia produktu. Tymczasem zmienność wymagań oraz trudność stworzenia optymalnej architektury od samego początku powoduje, że założenia architektoniczne należy cały czas monitorować oraz wprowadzać ewolucyjne zmiany, tak aby powstające rozwiązania były proste. Sztywna, nie zmieniana architektura najczęściej prowadzi do powstawania rozwiązań trudnych w zrozumieniu, naszpikowanych obejściami.

Ciągła refaktoryzacja

Przedstawiony proces przedstawia koncepcję ciągłej refaktoryzacji, w której jest ona częścią prac projektowych na każdym poziomie – architektury, projektu, kodu. Nie jest wydzieloną fazą, samodzielną częścią – jest częścią integralną prac. Chcę zaznaczyć, że powyższy proces (mimo, że tak został przedstawiony) nie jest całkowicie liniowy. Często realizując jeden z późniejszych kroków należy wielokrotnie wracać do kroków wcześniejszych. Przedstawiona strategia przedstawia bardziej kierunek działań niż sztywny algorytm.
Tagged under:

Naturalny porządek refaktoryzacji pod lupą cz. 4 Refaktoryzacja do wzorców

Stosując dotychczasowe kroki zaczynamy mieć coraz bardziej kształtne rozwiązanie, jednak głównie to co otrzymujemy to metody pogrupowane w klasy. Nadszedł czas na zastosowanie zasad obiektowych (np. zebranych w SOLID). Analizujemy kod pod kątem powtarzalności, potrzeby elastyczności, zapachów kodu i wprowadzamy wzorce projektowe. Często tam gdzie mamy wiele metod o zbliżonym schemacie będzie można zastosować Template Method lub Strategy. Tam gdzie tworzymy złożone struktury, tam stosujemy wzorzec Builder. Tam gdzie mamy do czynienia z nieskomplikowaną maszyną stanową wprowadzamy State. Tam gdzie potrzebujemy polimorficznego tworzenia obiektów używamy Abstract Factory lub jej zdegenerowanej postaci Simple Factory.
Jeśli przyjrzymy się klasie TextObfuscatorMethods z przykładu, możemy zauważyć, że większość metod przyjmuje jako parametr przetwarzany tekst. Jest to sygnał, że te metody powinny być w klasie, która ma pole zawierające przetwarzany tekst. W zasadzie możemy odwrócić sytuację i w klasie TextObfuscatorMethods umieścić pole typu przetwarzany tekst, wtedy metody staną się praktycznie bezparametrowe. Tym samym klasa nabiera charakteru wzorca Builder.
Inną opcją, pod warunkiem, że chcielibyśmy uzyskać możliwość dowolnego składania przekształceń zaciemniających tekst (stosować je wybiórczo, w dowolnej kolejności, o czym chcielibyśmy decydować w czasie wykonania), wtedy można by zastosować wzorzec Decorator.
Wersja z Budowniczym wyglądałaby jak poniżej.

public class TextObfuscator
{
    private ObfuscatedTextBuilder builder = null;
    // ...
    public string Obfuscate(string text)
    {
        builder.NewText(text);

        builder.ChangeWordsOrderRandomly();
        builder.AddMeaninglessWordsRandomly();
        builder.RemoveSeparators();
        builder.AddSeparatorsRandomly();
        builder.RemoveSpaces();
        builder.ReplacePolishCharactersWithNonPolish();
        builder.ReplaceUpperAndLowercase();

        return builder.ToString();
    }
}
public class ObfuscatedTextBuilder
{
    // ...
    private List textParts = new List();
    public virtual void NewText(string text)
    {
        this.textParts = ParseTextForWordsAndNonWords(text);
    }
    // ...
    public virtual void RemoveSpaces()
    {
        for (int i = 0; i < textParts.Count; i++)
        {
            RemoveSpacesFromTextPart(textParts, i);
            RemoveTextPartIfEmpty(textParts, i);
        }
    }

    public virtual void AddMeaninglessWordsRandomly()
    {
        for (int i = 0; i < textParts.Count; i++)
        {
            if (ShouldAddMeaninglessWords())
            {
                textParts.Insert(i, TextPart.SpaceSeparator); i++;
                textParts.Insert(i, new TextPart(DrawMeaninglessWord(), TextPartType.WORD)); i++;
                textParts.Insert(i, TextPart.SpaceSeparator);
            }
        }
    }
    // ...
}

sobota, 11 czerwca 2011

Tagged under: , ,

Naturalny porządek refaktoryzacji pod lupą cz. 3 Extract Method

W następnym kroku przyglądamy się odpowiedzialnościom poszczególnych klas oraz metodom, pod kątem dopasowania do odpowiedzialności klasy. Najlepiej przeanalizuj wszystkie metody oraz pogrupuj je pod kątem wykonywania podobnych operacji. Szukamy dla nich miejsca w innych klasach lub w nowej klasie. Pamiętaj: jeśli w danej klasie istnieje znacząca metoda prywatna (dłuższa niż 3-4 wiersze), to metoda ta powinna znaleźć się w innej klasie.
W naszym przykładzie metody głównie skupiają wokół pojedynczych operacji mających na celu zaciemnianie tekstu oraz zapewniającym wykonanie operacji z pewnym prawdopobieństwem. Możemy wyodrębnić odpowiadające tym operacjom klasy TextObfuscatorMethods oraz Probability.
Kod po tej zmianie mógłby wyglądać tak:
public class TextObfuscator
{
    // ...
    public string Obfuscate(string text)
    {
        List<TextPart> parts = methods.ParseTextForWordsAndNonWords(text);
        methods.ChangeWordsOrderRandomly(parts);
        methods.AddMeaninglessWordsRandomly(parts);
        methods.RemoveSeparators(parts);
        methods.AddSeparatorsRandomly(parts);
        methods.RemoveSpaces(parts);
        // ...
    }
}

public class TextObfuscatorMethods
{
    // ...
    public virtual void RemoveSpaces(List<TextPart> parts)
    {
        for (int i = 0; i < parts.Count; i++)
        {
            RemoveSpacesFromTextPart(parts, i);
            RemoveTextPartIfEmpty(parts, i);
        }
    }

    private static void RemoveSpacesFromTextPart(List<TextPart> parts, int i)
    {
        parts[i] = parts[i].ReplaceContents(" ", "");
    }

    public virtual void RemoveSeparators(List<TextPart> parts)
    {
        for(int i = 0; i < parts.Count; i++)
        {
            RemoveSeparatorsFromTextPart(parts, i);
            RemoveTextPartIfEmpty(parts, i);
        }
    }

    public virtual void AddSeparatorsRandomly(List<TextPart> parts)
    {
        for (int i = 0; i < parts.Count; i++)
        {
            if (ShouldAddSeparator())
            {
                parts.Insert(i, TextPart.NewSeparator(DrawSeparator())); i++;
            }
        }
    }

    private bool ShouldAddSeparator()
    {
        return probability.ShouldBeDone(ADDING_SEPARATOR_PROBABILITY);
    }
    // ...
}

public class Probability
{
    private Random random = new Random();

    public bool ShouldBeDone(double probability)
    {
        if (probability < 0.0 || probability > 1.0)
        {
            throw new ProbabilityException("Probability should be in range [0.0, 0.1]");
        }

        return random.NextDouble() < probability;
    }
}

wtorek, 7 czerwca 2011

Tagged under: ,

Czy myślałeś kiedyś o karierze trenera/konsultanta?

Jako że nasza działalność (BNS IT http://www.bnsit.pl) nabiera coraz większego rozmachu, pojawia się miejsce dla osób, które są chętne, aby pracować z innymi w celu dzielenia się wiedzą i doświadczeniem oraz chcą wspierać zespoły w zwiększaniu efektywności pracy.
Zatem jeśli masz doświadczenie praktyczne w pracy na projektami programistycznymi, jesteś kompetentny w PRZYNAJMNIEJ W JEDNYM z następujących obszarów:
- wzorce projektowe
- dobre praktyki pracy z kodem (refaktoryzacja, czysty kod, obiektowość (SOLID, GRASP, interfejsy))
- test-driven development
- domain-driven design
- scrum (agile)
- komunikacja w zespole (w kontekście projektów programistycznych),
- zarządzanie czasem dla programistów,
- zarządzanie projektami,
- zarządzanie wymaganiami, analiza.

Ważna jest wiedza i doświadczenie projektowe, co do warsztatu trenera, jesteśmy w stanie odpowiednio do tego przygotować.

Jesteśmy głównie zainteresowani stałą współpracą. Aczkolwiek ewentualnie opcja współpracy od czasu do czasu też wchodzi w grę.

Pisz śmiało: m [kroppka] sieraczkiewicz [mauppa] bnsit [kropkka] pl
Tagged under: , , ,

Naturalny porządek refaktoryzacji pod lupą cz. 2 Compose Method

Compose Method

Analiza metod takich jak przedstawiona w części 1. najczęściej prowadzi nas do zrozumienia, jakie są główne punkty algorytmu zawartego w metodzie. I to jest właśnie kolejny krok – postaraj się podzielić dużą metodę na mniejsze kroki wyodrębniając je do osobnym metod (refaktoryzacja Extract Method). Tym samym pierwotna metoda będzie się składać z sekwencji wywołań metod składowych. Przy odpowiedniej konwencji nazewniczej uzyskamy kod, który czyta się jak książkę.
Przy okazji warto co nieco posprzątać – głównie wprowadzić zmiany w mało intuicyjnych nazwach. Przykład znajdziesz poniżej.

public class TextObfuscator
{
    // ...
    public string Obfuscate(string text)
    {
        String result = ChangeWordsOrderRandomly(text);
        result = AddMeaninglessWordsRandomly(result);
        result = RemoveSeparators(result);
        result = AddSeparatorsRandomly(result);
        result = ReplacePolishCharactersWithNonPolishCharacters(result);
        result = ReplaceUpperAndLowerCharactersRandomly(result);
        result = RemoveSpaces(result);
        return result;
    }
    public String RemoveSpaces(String text)
    {
        return text.Replace(" ", "");
    }

    public String RemoveSeparators(String text)
    {
        return Regex.Replace(text, SEPARATORS_REGEX, "", RegexOptions.CultureInvariant);
    }


    public String ReplacePolishCharactersWithNonPolishCharacters(String text)
    {
        return text
                .Replace("ą", "a")
                .Replace("ł", "l")
                .Replace("ę", "e")
                .Replace("ń", "n")
                .Replace("ż", "z")
                .Replace("ź", "z")
                .Replace("ó", "o")
                .Replace("ś", "s")
                .Replace("ć", "c")
                .Replace("Ą", "A")
                .Replace("Ł", "L")
                .Replace("Ę", "E")
                .Replace("Ń", "N")
                .Replace("Ż", "Z")
                .Replace("Ź", "Z")
                .Replace("Ó", "O")
                .Replace("Ś", "S");
    }

    public String ReplaceUpperAndLowerCharactersRandomly(String text)
    {
        String result = "";
        char[] textArray = text.ToCharArray();

        foreach (char currentCharacter in textArray)
        {
            if (ShouldBeUpperAndLowerReplaced())
            {
                result += ReplaceLowerAndUpper(currentCharacter);
            }
            else
            {
                result += currentCharacter;
            }
        }

        return result;
    }
    // ...
}

piątek, 27 maja 2011

Tagged under: , , ,

Naturalny porządek refaktoryzacji pod lupą cz. 1

(Rozwinięcie tego wpisu powinno pojawić się w najbliższym SDJ http://sdjournal.pl/)


Refaktoryzacja to odwieczny (to chyba nie najlepsze słowo jak na krótki czas funkcjonowania dyscypliny inżynierii oprogramowania ;-)) problem - bo wszyscy wiedzą, że powinno się to robić, a nikt nie ma na to czasu. 
Jak już zaczniesz refaktoryzować, najczęściej nie wystarczy po prostu refaktoryzać, bo łatwo wpaść w szał refaktoryzacji, który polega na refaktoryzowaniu wszystkiego i za wszelką cenę. Przedstawię poniżej strategię refaktoryzacji, która w sposób systemowy pozwala podejść do tego często niewdzięcznego zadania, jednocześnie przyczyniając się do ewolucyjnego rozwoju projektu i architektury. Podpowiada też, jak się zabrać za kod odziedziczony i zacząć go refaktoryzować. Oto magiczne punkty:
1.   Zacznij od prostego rozwiązania, zgodnie zasadą Keep it simple stupid. Nie myśl zbyt dużo o wzorcach, o wprowadzaniu wszelkiej możliwej elastyczności.
2.   Z dużych metod zaczynaj wyodrębniać mniejsze składowe metody (refaktoryzacja Extract Method). Dąż do realizacji wzorca Compose Method – niech Twoja główna metoda składa się z serii wywołań mniejszych metod.
3.   Kiedy Twoje klasy będą składały się z dużej ilości małych metod, zacznij analizować odpowiedzialność klasy. Przesuń metody, które realizują inne odpowiedzialności do bardziej odpowiednich dla nich klas.
4.   Z czasem zaczynasz dostrzegać, że w Twoich rozwiązaniach potrzeba elastyczności – zacznij wprowadzać wzorce projektowe, tam gdzie potrzeba.
5.   Raz na jakiś czas (raz na kilka miesięcy przy większych projektach), analizuj to, co dzieje się z Twoim projektem. Architektura wymaga regularnego odświeżania i wprowadzania zmian, aby przystawała do pojawiających się wymagań.


Załóżmy, że mamy napisaną klasę udostępniającą metodą służącą do zaciemniania tekstu, będącego parametrem metody. Przykładowy kod znajdziesz na poniżej.
public class TextManager
{
    private TextManagerHelper _hlp = null;
    // ...
    public TextManager(TextManagerHelper _helper)
    {
        this._hlp = _helper;
    }

    private Random rnd = new Random();

    public string Convert(string text)
    {
        String result = "";
        List<TextPart> prts = _hlp.Convert(text);

        for (int i = 0; i < prts.Count; i++)
        {
            if (prts[i].Type.Equals(TextPartType.WORD) && rnd.NextDouble() < 0.2)
            {
                if (i + 2 < prts.Count)
                {
                    TextPart t = prts[i];
                    prts[i] = prts[i + 2];
                    prts[i + 2] = t;
                }
            }
        }

        for(int i1 = 0; i1 < prts.Count; i1++)
        {
            if (rnd.NextDouble() < 0.2)
            {
                prts.Insert(i1, new TextPart(" ", TextPartType.NONWORD)); i1++;
                String[] wds = new String[] { "i", "a", "aczkolwiek", "poniekąd" };
                int ind = rnd.Next(wds.Length);
                prts.Insert(i1, new TextPart(wds[ind], TextPartType.WORD)); i1++;
                prts.Insert(i1, new TextPart(" ", TextPartType.NONWORD));
            }
        }

        String result2 = "";

        foreach (TextPart part in prts)
        {
            result2 += part.Contents;
        }

        result = result2;
        result = Regex.Replace(result, @"[\?!-\.,:;'\(\)]", "", RegexOptions.CultureInvariant);

        String result1 = "";

        for ( int i2 = 0; i2 < result.Split(' ').Length - 1; i2++)
        {
            if (rnd.NextDouble() < 0.5)
            {
                char [] chs = new char[] { '.', ',', '!' };
                int j = rnd.Next(chs.Length);
                result1 += result.Split(' ')[i2] + chs[j];
            }
            else
            {
                result1 += result.Split(' ')[i2] + " ";
            }
        }
        result1 += result.Split(' ')[result.Split(' ').Length - 1];
        result = result1;

        result = result
            .Replace("ą", "a")
            .Replace("ł", "l")
            .Replace("ę", "e")
            .Replace("ń", "n")
            .Replace("ż", "z")
            .Replace("ź", "z")
            .Replace("ó", "o")
            .Replace("ś", "s")
            .Replace("ć", "c")
            .Replace("Ą", "A")
            .Replace("Ł", "L")
            .Replace("Ę", "E")
            .Replace("Ń", "N")
            .Replace("Ż", "Z")
            .Replace("Ź", "Z")
            .Replace("Ó", "O")
            .Replace("Ś", "S");

        String result3 = "";
        char[] tArray = result.ToCharArray();

        foreach (char ch in tArray) {
            if (rnd.NextDouble() < 0.3)
            {
                char? newCh = null;
                if (Char.IsLower(ch))
                {
                    newCh = Char.ToUpper(ch);
                } else
                {
                    newCh = Char.ToLower(ch);
                }

                result3 += newCh;
            }
            else
            {
                result3 += ch;
            }
        }

        result = result3.Replace(" ", "");

        return result;
    }
}



C. D. N.

wtorek, 19 kwietnia 2011

Tagged under: , ,

Czysty kod

Toczą się filozoficzne dyskusje, czy czysty kod ma znaczenie i czy inwestować czas w jego czytanie.
Nie będę się włączał w tę dyskusję bezpośrednio. Mały przykład:


prawo!Drugie!LEHMAna..oPiSujezJAWiskO!ZNanE.w,dziedZiNachi,wiEluI!!FIzyKi!zwieKszaNieNiEuPOrzAdKOwAnia!.W,.ENtropII. ,aCzkolwIEK! PONIekAD.!mIAre..uplYwU pONieKAd..czasuAczkOLWiek,
wpROWadZAnIe.dozMian!A,!aczkolWIeK !aNazywaNyCH,opROgrAmOWaniA ,iczesto !i!!prOgreSywnyMizwyKLE.poniEkAd,!PIeRWotna.StrUkturEnarusza.a.!PRoGramU.kUMulaCja,Zmian a.TEN,I !prOcEs!,,i.Tylko,liCzba,nasiLA, ,AcZkoLwiek,powiaZaniIntErakCjI poniEkAD!!roZnymi!ponIekad!,I!pomieDzYModuLamiw,sYStemiEZWiEkSZasie!.a, co! ,a,utruDniAzRoZUmienIe!!A,!acZKolwiek!I!..go,takzE!jEgO..modYfikacje..dalsze,
a,.jeST!,I.pOniEsieNie..aLternAtyWa,i.dodatKowyCh,WtrakciE!,nAKlaDOw,PielEgnaCjI..POswiecOnycHaczkOlwiek! ,na a,czynNosCi. ,a!ANTYREGREsYWnE!upRasZczaNie,StruKTuRy!i!LepszE.wprowadzaNyChwKOmPoNowANieI,.Zmian. ,iw,IsTNIejACE.i, rOwnoWaga.a,.OPrOgrAMOwanIe!pomIedZy,CzynnoscIami.,A,,ProGRESYwnyMI,RegresywNymI.od!.zaLeZYilOscI!.i, I.,RodZaju!infoRmaCji! a,plynacEJzwrOtnEj,srodoWiska!aCzkOlwieKZe,Inaczej!aczkOlwiEK!.,sa..pOnIEkad AobSLUGiwANE,zAdanIaZwiazaNe.Z,!.aczKOlWiEk!napraWa.blEdow!.a.!a!z,.inaczej. aCzkOLWIek!rozsZerzeniamI,funKcjOnalnyMi.
 Tak najczęściej wygląda nasz kod! A fe...!

Źródło: Wykład Bartosza Waltera o refaktoryzacji
Tagged under: ,

Zasada 20/80 Reloaded

Taki krótki wpis przyszedł mi do głowy.
Wiele osób zna i być może stosuje zasadę 20/80 (cały Agile na tym stoi!), która w uogólniona brzmi mniej więcej tak: "20% procent naszych działań generuje 80% efektów". Innymi słowy - "mniej więcej 20% tego co robimy, ma sens". Mocne, prawda? Proste?
Cholernie trudne! Bo jesteśmy przyzwyczajani i przyzwyczajeni do zapełniania czasu - żeby mieć poczucie, że cały czas robimy coś.
Ale nie o tym chciałem pisać. Chciałem zwrócić uwagę na fakt, że wiele osób (nie chcę używać słowa "większość", choć bym się nie pomylił zbyt wiele) niepoprawnie interpretuje to stwierdzenie.

Nie oznacza ono, że jak mamy 80% to już dalej nie warto poświęcać energii na dalszą pracę (projekt, release, funkcjonalność, zadanie, whatever)... ba! wtedy to już jest za późno! Właśnie zmarnowaliśmy ogromną ilość naszego cennego czasu. Bo co z tego, ze jest już 80% jeśli jest to zwykły shit, zadaniowa czy też funkcjonalna wata cukrowa!
Powyższa zasada oznacza: znajdź 20% tego czym warto się zajmować i jak to znajdziesz, to włóż w to 200% energii i zaangażowania. Resztę można olać, najlepiej renegocjując wykonanie "owej reszty".
Biznesowo: zamiast robić wielki system ze 100 ficzerami, lepiej zadać sobie pytanie, co naprawdę będzie użyteczne i co będzie używane i zrobić właśnie te 20 ficzerów.

P.S.
W DDD nazywamy to Core Domain! Zawsze bądź świadomy co jest Twoim corem! :) Zawsze zadawaj sobie pytanie, co jest Core w systemie, module, który robisz czy nawet funkcjonalności, którą realizujesz.

piątek, 25 lutego 2011

Tagged under: , , ,

Jaka nazwa dla tej metody?

public class OptionsAwareObject {  
    private Options options;  
      
    public void updateOptions(String fontName, int fontSize) {  
       options.setFontName(fontName);  
       options.setFontSize(fontSize);  
    }  
 }  
   
 class Options {  
   
    private int foregroundColor;  
      
    private int backgroundColor;  
      
    private String fontName;  
      
    private int fontSize; 
    // ...
}  

Czy updateOptions to dobra nazwa dla tej metody? Oczywiście, że nie! updateOptions jest ogólną nazwą, która sugerowałaby całościową aktualizacji opcji. Tymczasem my aktualizujemy tylko informacje nt. czcionki. Odpowiedzialnością metody jest zatem zmiana opcji związanych z czcionką. Lepszą nazwą byłaby:

public void updateFontOptions(String fontName, int fontSize) {  //...
Tagged under:

Antylitania do programistów

Nienawidzę Was za to że:
  • skupiacie się nad tym jakie ficzery ma Wasze IDE zamiast skupiać się na tym, jakich ficzerów potrzebuje Wasz klient/użytkownik,
  • klepanie w klawiaturę nazywacie myśleniem,
  • hektolitry czasu wyrzucacie w błoto testując manualnie swój kod,
  • więcej czasu spędzacie na walkę z frejmłorkami niż na dostarczaniu wartości dla końcowych użytkowników,
  • kodujecie wiele godzin nie zadając sobie pytania "co ja tak naprawdę robię",
  • naiwnie wierzycie, że technologie i narzędzia rozwiążą wasze problemy,
  • naiwnie wierzycie, że dobry algorytm jest ważniejszy od dobrego zrozumienia wymagań,
  • naiwnie wierzycie, że Wasza intuicja wystarczy w tworzeniu dobrego kodu,
  • naiwnie wierzycie, że jesteście w stanie ogarnąć złożoność kawałka systemu z którym pracujecie,
  • zgadzacie się na nierealne terminy,
  • że piszecie kiepski kod, znajdując na to wiele racjonalizacji (bo nie ma czasu),
  • w głowach tworzycie fragmenty kodu mimo że jeszcze dobrze nie wiecie co trzeba zrobić,
  • domyślacie się co trzeba zrobić, zamiast doprecyzować,
  • bezmyślnie podążacie za technologią, której używacie,
  • nie rozumiecie narzędzi i technologii, których używacie,
  • zamykacie się w swoim kawałku kodu, zrywając kontakt ze światem,
  • sądzicie, że to wszystko wina kierowników lub klienta i nie jesteście w stanie z tym nic zrobić.

... mimo że kocham bo sam jestem programistą.

sobota, 19 lutego 2011

Tagged under:

Call for ... code samples

Chętnie przedstawiłbym ideę naturalnego porządku refaktoryzacji na przykładowym kodzie. Niestety (co jest oczywiste rzecz jasna) nie mogę użyć kodu produkcyjnego klientów, a nie mam czasu przygotowywać do tego celu przykładu. Ale być może ktoś miałby chciałby na ten cel przeznaczyć swój kawałek kodu, projektu, który nie ma zbyt dużych (w zasadzie żadnych) obostrzeń co do publicznej publikacji.
Główne założenie to musi być w kodzie co refaktoryzować ;-)

W razie zainteresowania proszę o kontakt m [kropka] sieraczkiewicz [AT] bns [kropka] it.

wtorek, 15 lutego 2011

Tagged under: ,

Młody kierowniku/team liderze! Opamiętaj się!

Chyba nie napisze nic nowego... ale to tylko fakt, że historia lubi się powtarzać... bezustannie (vide: Marsz ku klęsce).

Otóż
Często powtarzanym, tragicznym w skutkach, nieprofesjonalnym działaniem drogi kierowniku/team liderze jest zobowiązywanie się do mało realnych terminów.

Naiwnie proste, można by rzec oczywiste, a jednak powtarza się. Dobrze o tym wiem, bo sam popełniałem nieraz ten błąd. Dlaczego zobowiązujemy się do mało realnych terminów:
a) bo jest presja kogoś z zewnątrz (klienta, menedżera, dyrektora),
b) bo wytwarzam presję wewnętrzną (chcę pokazać, że jesteśmy naprawdę dobrzy),
c) bo kompletnie nie mam pojęcia (albo mam pojęcie ale tylko trochę), co rzeczywiście trzeba zrobić w projekcie,
d) ??? - proszę o podpowiedzi :)

Co do pierwszych dwóch podpunktów rozwiązanie jest (wydaje się) proste - potrzeba asertywności i zwykłej szczerości wobec siebie i wobec przełożonych, tudzież klienta. Ta trudność, szczególnie jeśli się zaczyna (lub ma się niewielkie doświadczenie), wymaga często wyjście poza strefę komfortu - powiedzieć, że się nie da (w tym czasie).
I oczywiście można napotkać opór/sprzeciw, ale też najczęściej można pokazać swój profesjonalizm, który w tym przypadku zdefiniowałbym jako "Nie zobowiązuje się do niekonkretnych czy nierealnych terminów".
Często słyszę pytanie: "A jak zrealizować projekt, w którym już wiadomo, że jest za mało czasu, że za mało ludzi, że niejasne wymagania. Jakie są najlepsze praktyki". Odpowiedź jest prosta "Nie zgadzaj się na to, a raczej - negocjuj". Pokaż dlaczego termin jest nierealny. Podziel pracę i oszacuj. Pokaż ryzyka. Nie zgadzaj się na obniżenie jakości ("Jakoś damy radę - zrobimy bez testów, bez definiowania kryteriów akceptacyjnych, byle działało").
Ustalony termin to nie jest koniec świata i najczęściej można go negocjować, tylko uzbrój się w argumenty, które Ciebie przekonują.
Sam jestem szefem. Nieraz zdarzało mi się chlapnąć jakiś termin, bez większego przemyślenia. Moi pracownicy często się do niego przywiązują - czasami nikomu nie przychodzi do głowy, żeby negocjować przesunięcie terminu. Czasami wystarczy krótka dyskusja i sprawa załatwiona. W tym świecie niemal wszystko jest negocjowalne! Ale często wymaga to odwagi! :) Sprawdźcie - jedną z głównych wartości związanych z Agile'em jest Odwaga. Wiecie dlaczego? :)

Co do trzeciego podpunktu - zacznij używać jakiejś metodyki :) Skorzystaj z pomocy doświadczonego mentora.

środa, 9 lutego 2011

Tagged under:

Kilka nowych koncepcji - Mantra architektoniczna, Design Retrospective, Współdzielony kontekst, Naturalny porządek refaktoryzacji

Ostatnio wyewoluowało mi się w głowie kilka nazwanych koncepcji, a w zasadzie może po prostu je dobrze zrozumiałem :) Kilka notatek poniżej, które traktuje jako alpha draft ;-)

Mantra architektoniczna (ang. architectonic mantra)
To co dzieje się praktycznie w każdym projekcie to dryfowanie architektury, tak że po kilku (nastu) miesiącach trwania projektu mało kto w zespole jest w stanie dobrze określić, jakie operacje powinny być wykonywane np. przez kontroller (lub managedbean lub whatever), jakie przez obiekty dziedzinowe, jakie przez fasady, usługi. Z czasem mieszane są odpowiedzialności tychże klas i coraz trudniej z rozwojem systemu. Jedyną opcją w tym przypadku jest tzw. mantra architektoniczna, która jest wariacją nt. techniki zdartej płyty znanej z psychologii komunikacji. Polega ona na tym, że należy w prace zespołu włączyć ciągłe przypominanie jakie mamy w architekturze główne bloki i jakie są też ich odpowiedzialności. W tym przypadku również sugeruję zespołom, aby wprowadziły regularne spotkania (raz/mc, raz/sprint), które nazywam Design Retrospective podczas których zespół:
a) przypomina mantrę architektoniczną;
b) analizuje sytuacje, w których niejasne jest, gdzie powinny się znaleźć konkretne klasy, operacje, pola - na bazie tego co się działo ostatnio w projekcie
c) zbiera przypadki zdryfowania architektury i tworzy "program naprawczy"
d) podejmuje decyzję o świadomej restrukturyzacji architektury (to akurat rzadziej).

Współdzielony kontekst (ang. shared context)
Nie, nie, nie... bynajmniej nie chodzi tu o żadne wzorce projektowe czy architektoniczne. A o komunikację... Jak to powiedział Kent Beck gdzieś kiedyś "Większość problemów w projektach wynika z tego, że ktoś komuś czegoś nie powiedział". Zwolennicy Agile'a źródło problemów widzą w tzw. relay race, czyli przekazywaniu pracy między ludźmi (pewnie to znacie m. in. w takiej metaforze www.projectcartoon.com/).
Problem polega w tym, że w sytuacji przekazywania produktów (dokumentacji, kodu, wymagań itp.) pracy jednej osoby (grupy osób) innej, nie jest przekazywany kontekst informacji, który doprowadził do powstania tego produktu, często mylnie zakładając, że jest on nieistotny. Ja twierdzę, że jest kluczowy.
Ostatnio uczestniczyłem w takiej sytuacji. Jedna z osób dokonała wstępnej analizy wymagań i stworzyła podstawowy model systemu (model dziedzinowy). Mieliśmy przeanalizować ten model. Jednak nie posiadając kontekstu
(pewne przymyślenia, wnioski, informacje, mikrodecyzje), który powstał w głowie osoby, która go stworzyła dość trudno na głębszym poziomie niż tylko poprawność UMLowa, ocenić poprawność modelu. Taką sytuację nazywam brakiem współdzielonego kontekstu.
Dopiero zaczęliśmy wspólnie analizować wymagania w projekcie (User stories) i wspólnie budować model od nowa. Doszliśmy do podobnego modelu (z kilkoma subtelnymi różnicami), jednak w czasie "wspólnej pracy" powstał współdzielony kontekst (czyli uspójnione przemyślenia i rozumienie tematu). Współdzielony kontekst umożliwia trafniejsze podejmowanie decyzji oraz efektywniejszą kontynuację prac. To dlatego w Agile'u tak duży nacisk kładzie się na ścisłą współpracę - aby budować współdzielony kontekst.
I stąd jedna przestroga: Nie zarzucaj nikogo produktem swojej pracy, będącej efektem wielu godzin działań i/lub przemyśleń, oczekując, że go dobrze zrozumie. Przynajmniej pójdź i razem z nim go (ten produkt) przeanalizuj i wytłumacz jak do tego doszedłeś. A najlepiej wypracowuj to wspólnie :) Oszczędzisz wiele pracy sobie i tej osobie, a w szczególności unikniesz wielu zniekształceń komunikacyjnych (które to właśnie generują bardzo dużo dodatkowej, niepotrzebnej pracy).

Naturalny porządek refaktoryzacji (ang. natural order of refactoring) albo inaczej co to naprawdę znaczy ewolucyjny design lub architektura.
Jest to temat na osobny artykuł, tutaj więc pokrótce.
Od pewnego czasu krystalizuje mi się koncept systematyzacji procesu refaktoryzacji od najniższego poziomu abstrakcji do najwyższego - naturalny sposób na ewolucyjny rozwój designu (projektu systemu).
Idea jest taka - zaczynasz od najprostszego rozwiązania, które następnie refaktoryzujesz (jeśli potrzeba) na różnych poziomach abstrakcji (w pewnym ustalonym porządku). Oto porządek przekształceń (rozpoczynamy od niskopoziomowych).

0. Transformacje (triangulizacje) - jeśli używasz TDD (http://cleancoder.posterous.com/the-transformation-priority-premise)
1. Refaktoryzacje typu
Compose method (http://www.industriallogic.com/xp/refactoring/composeMethod.html) (czyli seria refaktoryzacji typu Extract method)
Czyli pierwszą implementację złożonej metody rozbijam na serię wywołań kilku metod składowych na tym samym poziomie abstrakcji. Z jednego bloku kodu powstaje kilka metod.
2. Refaktoryzacje typu Extract class, Extract interface, Move method, Move field na podstawie analizy odpowiedzialności klasy.
Z czasem wyodrębnionych metod w powyższym kroku jest coraz więcej, więc klasa zaczyna być zbyt duża i często przestaje spełniać regułę SRP (pojedynczej odpowiedzialności). Zatem zaczynasz myśleć o przeniesieniu tychże metod gdzieś indziej. Do innej klasy, do nowej klasy, być może temu będzie towarzyszyć wyodrębnienie się interfejsu, być może będzie trzeba poprzesuwać pola w klasie.
3. Refaktoryzacje do wzorców projektowych (http://www.industriallogic.com/xp/refactoring/)
W sytuacjach, kiedy musisz przygotować się na zmienność w projekcie (np. różne wersje algorytmów, przetwarzanie zdarzeń, adaptowanie struktur czy zachowania), wprowadzasz w projekcie wzorce projektowe.
4. Refaktoryzacja do wzorców architektonicznych (podstawowy opis wzorców http://martinfowler.com/eaaCatalog/)
Z czasem jeśli obecna architektura nie radzi sobie ze zmieniającymi się wymaganiami systemu albo potrzeba innej koncepcji, to modyfikujemy założenia architektoniczne - a zatem również musi ulec zmianie Mantra architektoniczna. Może to być: dodatnie warstwy, usunięcie warstwy, zmiana architektury dziedziny (Domain Model, Active Record, Exposed Domain Model) itp.


Heurystyka zwinnego modelowania systemu (czyli jak zacząć i mieć pewien zgrubny koncept)

Jak dojść zwinnie do wstępnego modelu, który pozwoli ruszyć z implementacją, a jednocześnie pozwoli uniknąć podstawowych błędów związanych z nieprzemyślaną architekturą? Jak i kiedy zaplanować architekturę? Zrobić wstępny projekt w podejściu zwinnym?
Oto propozycja zwinnej "drogi" :) Jest to miks praktyk Scrumowych/Agilowych, ICONIXa i naszych doświadczeń BNSowych.
1. Zdefiniuj (jeśli jeszcze nie masz) User Stories/Epics.
Zazwyczaj wystarczy pokrycie 80% systemu. Pamiętaj, żeby nie przesadzać ze szczegółami. Jeśli twój system ma UI - koniecznie korzystaj z draftów ekranów. Jeśli nie ma przydadzą się uproszczone diagramy określające interakcje systemu. Jakie diagramy? To już zależy od Twojej dziedziny :)
2. Przeanalizuj interakcje z systemami zewnętrznymi. Z nich też będą wynikać US. W efekcie usługi czy elementy modelu.
3. Określ priorytety i zgrubnie określ co wejdzie do Realeasu (wersji wdrożeniowej - czyli zazwyczaj efekt kilku miesięcy prac).
Pamiętaj, najbardziej będą interesować Cię rzeczy o wyższych priorytetach. Te o niższych nie są warte zbyt dużej uwagi.
4. Analizując po kolei User Stories, buduj model dziedziny (docelowo diagram klas dziedzinowych).
Skupiaj się przede wszystkim na nazwach klas i powiązaniach. Jest to najbardziej naturalny słownik dziedzinowy (bardziej definicja nazw).
5. Analizując po kolei User Stories (US), wyodrębniaj usługi (operacje) systemu.
Najlepiej, żeby były sformułowane w języku biznesowym. Na początku nie dziel ich specjalnie. Nawet wrzucaj do wspólnego worka. Dla ułatwienia możesz powiązać je z odpowiednimi US.
6. Określ złożoność operacji.
Określ, które operacje są bardziej złożone (wychodzą poza klasyczny CRUD). Będzie to podstawa do określenia kierunku architektury.
7. Wybierz kierunek architektury (w oparciu o model dziedzinowy)
(m. in. Domain Model, Anemic Domain Model, Active Record, Exposed Domain Model). Uwzględnij technologie, jeśli jest już wybrana lub ją wybierz.
8. Z wyróżnionych usług wybierz te o największym poziomie złożoności i ryzyku w celu analizy.
Zrób proste szkice klas, interakcji, które pozwolą przeanalizować złożoność. Wybieraj tylko te usługi do analizy, które przynależą do US o wysokich priorytetach lub też mogą znacząco wpłynąć na wykonalność projektu.
9. Osadź architekturę w technologii. Spartycjonuj usługi.
Rozrysuj bloki architektury i typowe klasy. Na bazie listy usług - podziel je grupując wg zbliżonych odpowiedzialności (spójnych dziedzinowo).
10. Przeanalizuj 1-2 US (koncept implementacji), które pozwolą zweryfikować założenia koncepcji.
Jeśli weryfikacja się nie powiedzie, wróć do 7 lub 8.
11. Zaimplementuj wybrane 1-2 US, aby zweryfikować koncepcje.
Może to wymagać wstępnego setupu projektu, środowiska. Może to być podstawa to tzw. Iteracji 0, Sprintu 0,
Elaboration phase (?? nie jestem ekspertem RUPa).
Jeśli weryfikacja się nie powiedzie lub koncept ma luki przejdź do 7 lub 8.
12. Zdefiniuj Mantrę architektoniczną.
13. Realizuj inkrementacyjnie projekt (to dalej definiuje metodyka).

Powiązany OFFTOPIC:
Zapodam pewną mądra myśl, która może kogoś natchnie do myślenia:
"W architekturze nie chodzi o rysunki, diagramy, powiązania z innymi systemami, ale o głębokie zrozumienie dziedziny i kontekstu systemu, z którym pracujesz. Zapomnij o UMLu, zapomnij o supernarzędziach wspierających Cię w projektowaniu, włącz myślenie. Włóż najwięcej energii w zrozumienie dziedziny. Ta wiedza już po nitce poprowadzi Cię do odpowiednich konstrukcji projektowych".
Tak, tak, to niewiele mówi. Na razie mogę odesłać do koncepcji Domain Driven Design oraz Lean Architecture (vide: Google).

niedziela, 6 lutego 2011

Tagged under: , , , ,

Nie powinieneś biegać ze złamaną nogą!

Jasne? Oczywiste? Niby tak się wydaje a jednak cały czas biegamy ze złamaną nogą...

Jedną z najczęściej oczekiwanych odpowiedzi, którą chcą usłyszeć osoby(w trakcie szkoleń lub projektów doradczych) - to odpowiedź na pytanie: "Jak mam sobie poradzić z pewnymi problemami w mojej sytuacji" (w moim projekcie, z moim klientem, menedżerem analitykiem whatever...). Jest to oczywiście naturalne pytanie, ale dużo ważniejsze jest pytanie: "Czy sytuacja, w której się znajduję jest zdrowa?". A najczęściej nie jest!
W takiej sytuacji, pierwotne pytanie można porównać do pytania: "Jak mogę szybko i efektywnie biegać ze złamaną nogą?". Chętnie poznam odpowiedzi...

Przykłady:
* Nie mamy dobrego kontaktu z osobą, która definiuje wymagania (bo między nami a klientem jest jeszcze firma pośrednia i dostajemy informacje z drugiej ręki, często nie mamy możliwości bezpośrednio skontaktowania się z taką osobą/osobami).

* Biznes/kierownicy projektu nie uwzględniają technicznych potrzeb podczas ustalania priorytetów (biznesowe priorytety są najważniejsze, ale techniczne nie mogą być ignorowane, gdyż spowoduję nadmierny wzrost kosztów implementacji).

* Nie ma nikogo, kto jest jednoznacznie odpowiedzialny za wymagania, definiuje je i odpowiada za zmiany (ponosi odpowiedzialność za ich koszt). Zazwyczaj odpowiedzialność ta jest rozmyta i wymagania pochodzą z nieskoordynowanych źródeł.

* Brak aktywnego udziału i wsparcia osób biznesowych podczas prac implementacyjnych (trudność w uzyskaniu konkretnych informacji wyjaśniających, jak to dokładnie ma działać).

* Brak refaktoryzacji, bo to kosztuje (za dużo i nikt za to nie zapłaci), bo to nie ma sensu (komu się uda posprzątać ten cały syf?).

* I wiele innych... chyba na każdy z tych tematów można poświęcić osobny artykuł.

Jak w takich sytuacjach zastosować coś rozsądnego? Jak w takim przypadku na przykład zastosować Scrum? (bo w takich sytuacjach najczęściej słyszę tego typu pytanie). Przypomnij sobie podstawowe pytanie: "Jak biegać ze złamaną nogą?"

Zanim zaczniesz dopasowywać się do zaistniałej sytuacji postaraj się wyeliminować z niej patologie! Zazwyczaj tego nie robimy bo:
* jest to trudne,
* często wymaga konfrontacji obecnej rzeczywistości,
* może doprowadzić początkowo do konfliktów i oporu,
* wymaga często dość sporych umiejętności komunikacyjnych (w tym negocjacyjnych),
* często będzie musiało to doprowadzić do zburzenia istniejącego status quo - a tego boi się prawie każdy ...
* zwyczajnie wymaga odwagi!

Więc zazwyczaj uczymy się biegać ze złamaną nogą. Jeeeeeeeeeee.... I like it. Na poziomie lokalnym udaje się wszystko poukładać (jakoś można z tym żyć), na poziomie globalnym jest to bez sensu. A jednak, tak zazwyczaj to funkcjonuje... Ciekawe, nieprawdaż? Niczym w amoku...

Zanim zaczniesz dopasowywać do obecnej sytuacji, krytycznie ją zweryfikuj! Jest pewna mądrość: "Automatyzacja nieprawidłowego procesu (czyli potencjalne jego przyspieszenie) zazwyczaj zwiększa jego nieefektywność". Jeśli sytuacja, w której się znajdujesz jest niezdrowa, czy można znaleźć w tej sytuacji najlepsze praktyki...? Jakie będą najlepsze praktyki biegania ze złamaną nogą? :)

Co zatem robić? To długa odpowiedź, często zależąca od subtelnych niuansów danego kontekstu. Tutaj nawet nie podejmę próby jej sformułowania. Ten post ma sprowokować do myślenia! Jest spora szansa, że jak zaczniesz analizować sytuację (sam/z osobami współpracownikami) - znajdziecie odpowiedzi. Pod warunkiem, że nie będziecie szukać czarodziejskiej różdżki, która bezszelestnie, bezinwazyjnie, bezproblemowo wszystko załatwi. Jeszcze raz przypomnę - próba rozpoczęcia zmian w tym kierunku wymaga ODWAGI i z pewnością wykroczenie poza własną STREFĘ KOMFORTU.

Mały hint. Jeśli w dużym przybliżeniu założymy taki łańcuch (pozwól, że pominę uzasadnienie tego pogrupowania, żeby nie wydłużać posta, może kiedyś będzie okazja...):

programista/tester/grafik... => analityk/kierownik => menedżer/klient/kierownik po stronie klienta

Największą moc sprawczą mają oczywiście ostatnie elementy łańcucha, ale też zazwyczaj im najtrudniej dostrzec patologie, albo też bardzo często są ich źródłem (jako osoby decyzyjne). Tej grupie najłatwiej będzie doprowadzić do potrzebnych zmian.
Najtrudniej mają osoby z początku tego łańcucha (gdyż są najmniej decyzyjne, przez to inicjowane zmiany wymagają często najwięcej odwagi!). Dla nich jedną ze skuteczniejszych (choć często żmudnych i długotrwałych) technik jest technika zdartej płyty :) (czyli ciągłe wskazywanie patologii, pokazywanie ich konsekwencji i upierdliwe o tym przypominanie... niestrudzenie, bez przerwy). Jeśli przy tym uda się zbudować koalicję w naszym łańcuchu, tym większa szansa na zmiany.

Jest jedna opcja - można poszukać pomocy z zewnątrz. Analiza, rekomendacje zewnętrznej niezależnej, obiektywnej osoby/firmy mogą skatalizować proces zmian.

Pamiętaj! Złożenie złamanej nogi może boleć!