Archive for the 'IT projects' Category

Jak sobie ułatwić pisanie aplikacji ?

poniedziałek, styczeń 1st, 2007 by Paweł Rutkowski

Wstęp

Obecnie pisanie aplikacji wygląda w zupełnie inny sposób niż kiedyś. Terminy są bardziej napięte, Klienci bardziej wymagający, kod coraz większy i sami mamy też wobec własnego kodu coraz większe wymagania. Jak do tego dorzucimy kilka równolegle rozwijanych wersji to już zupełnie można się w tym wszystkim pogubić.

Dlatego też chciałbym przybliżyć Wam kilka aplikacji bez których nie wyobrażam sobie obecnie developmentu. O systemach kontroli wersji już pisałem więc nie będę się powtarzał. Opowiem o trzech narzędziach: trac, Todo (zwany też devTodo) oraz SVNmerge.

Trac

Trac jest aplikacją webową integrującą się z repozytorium Subversion. Posiada wbudowaną przeglądarkę repozytorium, wiki dotyczące danego projektu, system ticketów (bugów) oraz tzw Timeline zawierający chronologiczne informacje dotyczące commitów, dodanych ticketów, zamknięcia bugów itp.

Aplikacja wygląda ładnie (w przeciwieństwie do BugZilli, której wygląd wręcz straszy :). Przy odrobinie nakładu pracy ładnie się integruje ze środowiskiem wieloużytkownikowym, co jest bardzo ważne przy śledzeniu błędów w projekcie i ich naprawianiu. Integracja z Subversion pozwala na używanie automatycznych linków. Np.: jeżeli w commicie jakiejś poprawki napiszemy “poprawka #43″ to przy wyświetlaniu danych z tego changesetu (commity w Subversion są nazywane changesetami czyli zestawem zmian, wgranych do repozytorium) w opisie automatycznie pojawi się link do strony z ticketem numer 43. Drugi przydatny auto-link służy do oznaczenia changesetu - np.: r166 lub [166] automatycznie stworzą link do strony zawierającej informacje na temat tej zmiany. Wśród tych informacji można zobaczyć które pliki zmieniono (i jakie zmiany zawierały), które dodano a któe usunięto - oczywiście jest dostępny także opis danego commita.

Kolejną ciekawą funkcją jest możliwość tworzenia ticketów i przypisywania ich do milestone’ów (zazwyczaj jest to moment w projekcie kiedy uzyskuje on konkretną funkcjonalność) i śledzenie ile pracy jeszcze trzeba wykonać aby osiągnąć dany punkt. Pozwala to na lepsze planowanie implementowanych funkcjonalności jeżeli widzimy zbliżający się deadline.

Wiki jest pomocne przy tworzeniu dokumentacji projektowej. Ponieważ każdy z developerów może edytować strony - przy zachowaniu odpowiedniego poziomu dyscypliny - będzie ona zawsze aktualna i obszerna. Mechanizm ten jest znany z Wikipedii więc nie będę się nad nim rozwodził.

Każdy projekt który zaczynamy ma automatycznie tworzony serwis opraty na tracu do śledzenia tego projektu i nie wyobrażam sobie pracy bez narzędzia tego typu. Na rynku istnieje oczywiście kilka podobnych rozwiązań ale żadne z mi znanych nie integruje się tak dobrze z Subversion i nie jest tak kompletne. Jedyną wadą trac jest brak możliwości zgłaszania problemów/błędów w aplikacji przez e-mail (choć można to “doskryptować”).

Strona projektu: http://trac.edgewall.org/

devTodo

Drugim narzędziem z którego korzystam jako developer jest devTodo. Przez długi czas szukałem narzędzia które umożliwiało by mi bardzo szybkie dodanie nowych zadań do listy. Ponieważ pracuje głównie na konsoli, szukałem czegoś co by pracowało w trybie tekstowym na serwerze. No i znalazłem. devTodo jest właśnie prostą listą zadań z priorytetyzacją zadań, napisaną w C, pracującą w trybie tekstowym. Sprawdza się idealnie gdy nie chcę przerywać pracy żeby zapisać jakąś rzecz do zrobienia. Przy połączeniu z wpisywaniem zadań “ogólnych” do traca tworzą cudowny tandem.

Ciekawą opcją jest możliwość podczepienia skryptu pod powłokę bash który automatycznie wyświetli listę zadań z pliku .todo przy wejściu do dowolnego katalogu go zawierającego. Dzięki temu trudno jest zapomnieć co się ma do zrobienia.

Podczas pisania tego artykułu przyszedł mi do głowy pomysł na integrację traca i todo. Można napisać skryp który wyciągnie przypisane tickety do danego użytkownika i doda je do listy devTodo. Drugi skrypt sprawdzał by które zadania zostały rozwiązane i uaktualniał wpisy w tracu.

SVNmerge

Ostatnie narzędzie jest mniej uniwersalne w przeciwieństwie do dwóch poprzednich. Wręcz jest narzędziem które pomaga w specyficznych sytuacjach. Mianowicie musisz używać Subversion i Twoj projekt musiał rozdzielić się kiedyś na dwie gałęzie (np.: w wyniku rozpoczęcia prac nad klonem serwisu). Jeżeli byłeś kiedyś w takiej sytuacji wiesz ile trudu zajmuje przenoszenie zmian pomiędzy wersjami. Jest to bardzo trudna i żmudna praca. Na szczęście jest narzędzie które to ułatwia. Jest to skrypt napisany w pythonie, dostępny w standardowej dystrybucji subversion w katalogu contrib.

Narzędzie to znacząco ułatwia przeglądanie zmian które nie są przeniesione do drugiej wersji, ich nanoszenie oraz blokowanie zmian które nigdy nie powinny być przeniesione. Skrypt ten jest generalnie nakładką na podstawowe narzędzia subversion, usprawniając ich użycie. Informacje na temat wszelakich zmian są umieszczne repozytorium Subversion (z wykorzystaniem mechanizmu svn:properties”). Jeżeli Twój projekt spełnia założenia użycia SVNmerge, polecam skorzystanie z niego - tak jak kiedyś starałem się nie rozdzielać projektów w czasie prac developerskich, tak od momentu kiedy go używam nie stanowi to dla mnie problemu.

A czy Wy znacie jakieś narzędzia które ułatwiają pisanie (pomijam edytory :) którymi warto było by się zainteresować?

Jak zrobić mechanizm pluginów w Ruby

wtorek, grudzień 12th, 2006 by Paweł Rutkowski

Podczas pracy przy ostatnim projekcie napisałem kilka programów w Ruby. Jeden z nich był frameworkiem udostępniającym pewne API. Głównym zadaniem tego frameworku było usprawnienie operacji na bazie SQL które co jakiś czas musieliśmy wykonywać ręcznie - co było bardzo pracochłonne i wymagało wielkiej uwagi.

Założenia dla frameworku były następujące:

  • udostępniać minimalną funkcjonalność - wspólną dla innych operacji
  • obsługiwać pluginy które realizowały by poszczególne operacje
  • pluginy powinny “myśleć” za użytkownika, tzn weryfikować dane ew. podpowiadać dostępne rozwiązania (ale to będzie temat na oddzielny wpis)

Musiałem zatem wymyślić jakiś mechanizm pluginów. Ponieważ Ruby jest językiem bardzo elastycznym nie było z tym większego problemu.

Pierwszą rzeczą było stworzenie klasy Action - z której będą dziedziczyć wszystkie pluginy. Następnie zaimplementowałem dwie metody: self.description i self.run. Pierwsza z nich była odpowiedzialna za wyświetlanie opisu pluginu, druga zaś uruchamiała właściwy plugin. Ponieważ klasa Action sama w sobie nie była wykorzystywana powyższe metody zwracały tylko informacje że należy je zaimplementować w klasie dziedziczącej.

Drugą rzeczą było udostępnienie jednego z obiektów dla każdego z pluginów. Tutaj sprawa nie była skomplikowana - wystarczyło dodać parametr do konstruktora, przypisać do zmiennej zaś przy tworzeniu obiektu przekazać odpowiednią zmienną.

Oto jak wygląda w uproszczeniu ta klasa


class Action
def initialize(api_object)
@api = api_object
end
def self.description
puts "desc method have to be overriden"
end
# method execute to run plugin
def run
puts "run method have to be overriden"
end
end

Narazie prosto prawda ? No to teraz trzeba było by jakoś ładować pluginy do aplikacji. Stwierdziłem że najlepszą metodą będzie utworzenie katalogu plugins i wczytywanie wszystkich plików które się tam znajdują (z definicją nowej klasy która de facto jest pluginem). Do tego posłuzyłem się następującym kawałkiem kodu:


def load_actions
Dir["plugins/*.rb"].sort.each { |plugin|
require plugin
}
end

I zaraz potem trafiłem na problem. Jak wyciągnąć klasy które dziedziczą z innej klasy ? Pomimo że ruby ma bardzo dobrze rozwinięte “reflections” (jest to możliwość dynamicznego metaprogramowania - np.: sprawdzanie dostępnych metod w obiektach, informacji o zmiennych, ich typach itp) to nie udało mi się znaleźć funkcji oferującej taką funkcjonalność. Trochę czasu mi zajęło przejrzenie metod dostępnych w klasach Object, ObjectSpace, Class i wyszukanie tych z których można skorzystać. Oto jak wyglądał kod (a pod spodem jego omówienie).


class Class
def self.find_by_super(sclass)
ret = []
ObjectSpace.each_object(Class) { |klass|
ret << klass if klass.superclass == sclass
}
return ret
end
end

Jak widać powyżej do klasy Class została dodana metoda find_by_super która jako parametr przyjmuję klasę nadrzędną w stosunku do klas które szukamy. Następnie przechodzimy przez wszystkie obiekty typu Class (czyli wszystkie klasy) i sprawdzamy które z nich dziedziczą z klasy określonej w parametrze funkcji.

Właśnie podczas pisania tego postu zauważyłem że można zrobić z powyższego kodu “jednolinijkowca”.


def self.find_by_super(sclass)
ObjectSpace.each_object(Class).find_all { |klass| klass.superclass == sclass }
end

Następnie należało już tylko dokonać iteracji po pluginach i wyświetlić ich listę w celu dokonania wyboru. Trick polegał na załadowaniu listy pluginów do tablicy, ich wyświetlenie a następnie zinterpretowanie wejścia użytkownika jako indeksu do konkretnego miejsca w tablicy, wskazującego na plugin który go interesuje.


[...]
# szukamy klass < Action
@actions = Class.find_by_super(Action)
[...]
# licznik na 0
n=0
actions.each { |action|
# dla kazdej akcji wywolujemy jej metode
# description i pokazujemy na ekranie
puts "#{n} - #{action.description}"
# zwiekszamy licznik :)
n=+1
}
[...]
printf("Please choose action: ")
# odczytujemy co podal user
@user_choose = $stdin.gets.to_i
# tworzymy obiekt na podstawie wybranej klasy i
# do konstruktora przekazujemy @api
action = @actions[user_choose].new(@api)
[...]
action.run

Proste ? Pewnie po przeczytaniu tego artykułu tak :) Natomiast dla mnie było to swego rodzaju wyzwanie, które trochę czasu zajęło. Było to jednak ciekawe zagadnienie a dzięki Ruby i mozliwością tego języka bardzo przyjemne do zaimplementowania. Oczywiście cały framework oferował znacznie więcej i pojawiło się wiele innych problemów, ale nie chciałem ich tu zamieszczać żeby nie zaćmić ogólnej ideii.

RT i konwersja SQLite do MySQL

czwartek, wrzesień 28th, 2006 by Paweł Rutkowski

Po dość długiej przerwie, nowy wpis :)

Wstęp

Ostatnio wdrażałem w jednej firmie system obsługi zgłoszeń serwisowych - tzw. ticketów. Mój wybór padł na RT: Request Tracker. Jest to narzędzie open-source, oparte o interface webowy. Zdecydowałem się na nie ze względu na łatwą integrację z mailami, co bardzo ułatwiło wdrożenie (szczególnie ze strony Klientów firmy) i nie narażało na koszty szkoleń. Wkońcu każdy potrafi wysłać maila na określony adres :)

Prognozy… mylne…

Przy instalacjach tego typu ruch zazwyczaj jest mały, jak i ilość danych w systemie nie jest oszołomiająca. Dlatego też zdecydowałem się użycie bazy SQLite co by niepotrzebnie nie instalować nowych usług na serwerze. Niestety… popełniłem błąd. Po miesiącu już można było odczuć dyskomfort przy pracy z systemem, natomiast po trzech była to prawdziwa katorga. Ilość danych która się pojawiła w systemie, całkowicie mnie zaskoczyła.

Trzeba sobie radzić…

Troche czasu mi zajeło wymyślenie sposbu migracji. Nie udało mi się znaleźć żadnych gotowych narzędzi do konwersji tak dużych baz sqlite do mysqla, a po mimio że to SQL to jednak dialekty troche inne. Rozwiązanie było dość trywialne:

  1. Zainstalowanie MySQL i skonfigurowanie go (założenie bazy, usera, itp)
  2. Załadowanie schema.mysql z dystrybucji RT do bazy
  3. Napisanie prostego skryptu w ruby, który dla każdej tabeli w bazie wyciągnął wszystkie rekordy i na ich podstawie stworzył inserty.
  4. Załadowanie insertów do bazy

skryt wyglada w następujący sposób

      1 require 'sqlite3'
      2 class String
      3    def escape
      4       x = self.gsub(/\\'/,"' ").gsub(/\\/, "\\\\\\").gsub(/[']/,"' '")
      5    end
      6 end
      7 tabele = ['ACL', 'Attachments', 'Attributes',
      8     'CachedGroupMembers', 'CustomFieldValues',
      9     'CustomFields', 'GroupMembers', 'Groups',
     10     'Links', 'ObjectCustomFieldValues', 'ObjectCustomFields',
     11     'Principals', 'Queues', 'ScripActions', 'ScripConditions',
     12     'Scrips', 'Templates', 'Tickets', 'Transactions', 'Users']
     13
     14 db = SQLite3::Database.new("/usr/local/rt3/var/rt3")
     15    puts "begin;"
     16    tabele.each { |t|
     17    rows = db.execute("select * from #{t}")
     18    rows.each { |row|
     19       d =[]
     20       row.each { |col|
     21          if col.nil?
     22             d << '\'\''
     23          else
     24             d << '\'' + col.escape + '\''
     25          end
     26       }
     27       puts sprintf("insert into #{t} values (%s);\n", d.join(','))
     28    }
     29 }
     30 puts "commit;"

Po tym zabiegu, RT dostało ogromnego przyśpieszenia. Ja natomiast chyba już nigdy więcej nie wybiore SQLite jako bazy do jakiegokolwiek produkcyjnego systemu…

Czy należy się bać routingu BGP ?

niedziela, lipiec 16th, 2006 by Paweł Rutkowski

Krótka odpowiedź - nie

BGP jest protokołem routingu. Pozwala on na podłączenie kilku różnych łączy i kierowanie przez nie ruchu w zależności od naszych potrzeb. Podstawowe zastosowania:

  1. Kilka łączy (np. backupowe)
  2. Łącza asymetryczne (np.: jedno ma przepustowość X, drugie zaś X/4)
  3. Łącza do różnych providerów (np.: chcemy aby ruch do sieci GTS szedł przez jedno łącze, a ruch do Futuro przez drugie)

Ostatnio sam stanąłem przed potrzebą uruchomienia BGP, obejżałem kilka dostępnych rozwiązań software’owych. Na pierwszy ogień poszedł pakiet Quagga (swego czasu zwanym Zebra). Jest to dość stary pakiet, ale odziwo nie odbija się to dobrze na jego stabilności. Potrafi on czasem zrobić bardzo duże problemy (np.: process zginie samoistnie czyszcząc całą tablicę routingu). Może to spowodować całkowite odłączenie maszyn pracujących w sieci. Dodatkowo jest bardzo ma bardzo duże zapotrzebowanie na pamięć (co może świadczyć o błędach wycieku pamięci ).

Ponieważ nie moge sobie pozwolić na niestabilne działanie systemów, musiałem poszukać alternatywy. Zainteresowałem się OpenBGPd. Jest to dość młody projekt ze stajni OpenBSD. Wersja której użyłem jest oznaczona numerkiem 3.9. Odrazu widać przewage nad Quagga - process jest mały i w porównaniu z nią zapotrzebowanie pamięci jest małe. Dodatkowo działa stabilnie, zaś konfiguracja jest prosta i przyjemna (w zależności od konfiguracji ISP - może się ograniczać tylko do pisania ok 20 krótkich linijek tekstu).

Co do samego mechanizmu BGP - jeżeli nie ma się łącza na nim opartego, wydaje się on bardzo abstrakcyjny. Natomiast jeżeli już się z nim zacznie pracować to okazuje się że wszystko jest proste i oczywiste. Zatem jeżeli masz możliwość rozpoczęcia przygody z BGP, bardzo mocno zachęcam (o ile użyjesz OpenBGPd). Oprócz tego że jest to ciekawa zabawa, to daje potężne możliwości zarządzania ruchem.

Książki o zarządzaniu projektami i projektowaniu systemów

piątek, maj 26th, 2006 by Paweł Rutkowski

Poniżej kilka książek które polecam:

Szczególnie polecam dwie pierwsze pozycje. “Dom wariatów” opisuje zagadnienia związane z projektowaniem systemów pod kątem ludzkim - jakie problemy mogą stanąć przed użytkownikami i czym są spowodowane. Mała uwaga: Alan Cooper mniej więcej od połowy ksiązki pokazuje swoje bardzo negatywne nastawienie do programistów - dla mnie było ono bardzo denerwujące bo przy każdej możliwej okazji rozpisywał się jacy “trudni” są programiści.

Drugą pozycja jest o dziwo opowieścią fabularną której akcja dotyczy tworzenia zaawansowanego projektu dla pewnej korporacji. Czyta się to naprawde dobrze ponieważ liczba technikaliów jest ograniczona do minimum.

Być poetą kodu: pisanie serwera w Ruby a refaktoring

piątek, maj 26th, 2006 by Paweł Rutkowski

Zanim zacznę cokolwiek opisywać, wyjaśnię pojęcie “refaktoring“. Jest to proces króry odnosi się głównie do projektowania i programowania systemów. Istotą tego procesu jest wprowadzanie zmian w projekcie, które nie rozszerzają funkcjonalności projektu ale podnoszą jego standard architektoniczny. Dokonywuje się modyfikacji różnych funkcji, obiektów czy algorytmów w celu zwiększenia ich przejżystości (czy w przypadku programowania obiektowego - hermetyczności obiektów). Zazwyczaj ten proces jest pomijany ze względu na dodatkowy czas jaki trzeba poświęcić na jego przeprowadzenia, a większość managerów nie widzi takiej potrzeby - skoro coś działa to po co to zmieniać.

Jak pisałem serwer

Ostatnio pisałem serwer sieciowy który miał przyjąć pewne dane, przetworzyć je i zwrócić odpowiedni status. Zadanie nie było specjalnie trudne, gdyby nie fakt iż przewidziano dowolne rozszerzanie procedur przetwarzania danych.

Ponieważ była to pierwsza aplikacja serwerowa jaką pisałem w Ruby wyszedłem z założenia że najlepiej będzie napisać ją metodą przyrostową. Dzięki temu odrazu mogłem testować funkcjonalność i nanośić szybko modyfikacje. Wadą było oczywiście to że model systemu był nie do końca dobrze przemyślany (np.: system komunikatów przesyłanych pomiędzy klientem a serwerem).

Pierwszą działającą wersję miałem dość szybko. Była w stanie nasłuchiwać na zadanym gnieździe, odbierać jakieś dane i odsyłać odpowiedzi. Tutaj trafiłem na pierwszy problem. Dopóki komunikacja polegała na przesyłaniu pojedyńczych lini tekstu, to nie było problemu (funkcje puts i readline). Ale w momencie gdy potrzebowalem przesłać nie znanej długości tekst pojawił się problem. Mogłem odczytywać dane w pętli aż do napotkania jakiegoś znacznika, ale znacząco to wpływało na wydajność serwera ( nie wiem dlaczego). Zamiast tego zdecydowałem się wysyłać komunikat że przygotowałem do wysłania X bajtów i serwer po odczytaniu tego komunikatu odczytywał z gniazdka ilość bajtów przekazanych przez klienta.

Następnym problemem była obsługa kilku połączeń jednocześnie. Wypróbowałem kilka metod o których dowiedziałem się z doskonałej książki “UNIX. Programowanie usług sieciowych - tom 1 - API: gniazda i XTI” i najwydajniesza okazała się metoda “preforking” z accept( w sumie to dziwne, ale jeszcze to sprawdze). Zaimplementowanie roznych metod, ich benchmark, tuning zajeło mi troche czasu, ale nie było bardzo trudne.

Następnym krokiem było przetwarzanie informacji. To była już prostsza rzecz ponieważ tego typu parsery pisałem wcześniej w PHP, Pythonie ale też i Ruby.

Jak doszedłem do refaktoringu

Miałem już cały serwer. Spojżałem na jego kod i sie przeraźiłem. W kilku miejscach zduplikowana funkcjonalność, kod który bardziej przypominał programowanie strukturalne
niż obiektowe. Nie mogłem tego tak zostawić, więc wziąłem się za przepisywanie kodu. Rozplanowałem sobie nową strukture klas, strukture komunikatów, wszystkie potrzebne modele. Wydzieliłem także trzy podstawowe moduły:

  • Server - którego zadaniem była inicjalizacja gniazdek i pre-forking
  • Worker - jego zadaniem była obsługa komunikacji z klientem
  • Processor - on odpowiadał tylko za przetworzenie danych

Pierwsze rzeczą która mnie zachwyciła to fakt iż refaktoring w Ruby jest bardzo prosty. Wystarczyło przekopiować odpowiednie kawałki kodu do odpowiednich klas i dokonać ich drobnej modyfikacji.

Drugą natomiast jak błyskawicznie poprawiła się czytelność kodu. Wykorzystałem praktycznie większość zalet programowania obiektowego, przez co każdy obiekt operował na innym. Dało mi to wielką swobodę i pozwoliło uproscić także kod klienta poprzez współdzielenie niektórych modeli.

Ile czasu zajął mi refaktoring ? Myślę że całość zajeła mi ok 3h. Długo ? Raczej długo, natomiast architektura jaką sobie stworzyłem pozwala mi teraz na praktycznie dowolne modyfikowanie kodu które nie będzie wpływało znacząco na sam serwer.

Celem refaktoringu jest właśnie uproszczanie struktury programu i podnoszenie stanardu kodu. Aby móc to docenić trzeba być “poetą kodu który nie tylko zwraca uwagę na to czy kod działa, ale także czy jest on prosty, czytelny, zrozumiały i funkcjonalny. Nie znam zbyt wielu programistów którzy potrafili by to docenić.

Na sam koniec pozwolę sobie odnieść się trochę do biznesu. Niestety nie zawsze mamy czas i pieniądze żeby “tkać złotą nić, błękitnego kodu“. Jeżeli mamy ograniczony czas i budżet powinniśmy na samym początku projektu przyjąć pewien standard kodowania i wymagać trzymania się jego przez programistów. Natomiast refaktoring przeprowadzać tylko dla krytycznych częsci systemu które podejżewam że będą w przyszłości wymagały modyfikacji. Ja osobiście staram się przewidzieć w budżecie koszty refaktoringu, ponieważ jednak uważam się za “poetę kodu”.

UPDATES

26/05/2006 16:28

Sprawdziłem jeszcze raz różne modele obsługi równoczesnych połączeń i jednak wydajniejsze są wątki (Thread). Ruby na FreeBSD źle obsługuje mechanizm Copy-On-Write przez co każdy forkowany proces zabiera bardzo dużo pamięci (szczególnie że użyłem ActiveRecord). Tak więc 5 procesów RoR zabierało bagatela 100MB pamięci.

Dużo lepszym rozwiązaniem okazało się użycie wątków. Przeprowadziłem szybki refaktoring w wyniku czego otrzymałem (w uproszczeniu) taki kod:


# w @server mamy odpowiedni (TCP|UDP|UNIX)Server
loop {
Thread.start(@server.accept) { |socket|
Worker.run(socket)
}
}

Proste do implementacji i wydajne. Zmiany w stosunku do mojego poprzedniego kodu sprowadzały się w zasadzie do wymiany fork na Thread.start.

Jakie narzędzia użyć do tworzenia dokumentacji ?

niedziela, kwiecień 30th, 2006 by Paweł Rutkowski

Dokumentacja ? Po co ?

Każdy projekt IT trzeba w jakiś sposób dokumentować. Czasem jest to tylko dokumentacja dla użytkownika końcowego, czasem tylko dokumentacja API. Niestety większość osób wogóle nie dokumentuje swoich produktów (mówie to głównie o freelancerach, ponieważ firmy zazwyczaj podchodzą do tego znacznie poważniej). Wszystko jest wporządku dopóki Klient, który posługuje się danym programem, pamięta jak się go obsługuje. Problem pojawia się jak zapomni - zamiast wcześniej przygotować mu dokumentacje, będziemy musieli co chwila odpowiadać na jego pytania.

Rodzaje dokumentacji

Zanim opisze narzędzia wspomagające proces tworzenia dokumentacji, wyróżnię kilka typów dokumentacji:

dla użytkownika końcowego
Przeznaczona jest tylko dla użytkownika, który będzie korzystał z oprogramowania. Musi być zrozumiała dla typowego użytkownika i nie powinna wychodzić poza jego dziedzinę. Nie opisuj swojego cudownego modułu komunikacji z bazą danych - zwykłemu użytkownikowi taka wiedza nie jest potrzebna.
dla administratora systemu (zwana także maintenance manual)
Zawiera informacje istotne z puktu widzenia administratora, który będzie zajmował się bierzącą obsługą systemu od strony technicznej. Powinny się tu znaleźć informacje o konfiguracji oprogramowania, wymaganiach sprzętowych, czynnościach konserwacyjnych.
opis zewnętrznego API
Zawiera opis funkcji używanych przez zewnętrzne programy do komunikacji z systemem. Jest głównie tworzona w przypadku systemów które będą integrowane z zewnętrznym oprogramowaniem.

wewnętrzna
Zawiera dokumentację opisującą wnętrze systemu. Zarówno jego funkcje, jak i data-flow czyli przepływ informacji pomiędzy poszczególnymi modułami. W niej także powinny się znaleźć opisy, wyjaśniające dlaczego dane rozwiązanie programistyczne zostało wykorzytsane. W przypadku skomplikowanych funkcji powinno się je opisać w sposób zrozumiały dla więszości zespołu. Ten typ dokumentacji zazwyczaj nie opuszcza deweloperów (pewnie ze względu na dużą ilość słów niecenzuralnych:)

Narzędzia

Postaram się przybliżyć kilka metod czy narzędzi ułatwiających tworzenie dokumentacji. Niektóre mają dość specyficzne zastosowanie, inne są wmiare ogólne. Konkretny wybór pozostawiam Wam.

Oto dwa założenia którymi się kierowałem przy wyborze narzędzi:

  • Musi pozwalać na edycję przez wielu użytkowników
  • Musi pozwalać na wyświetlenie różnic pomiędzy wersjami
  • Musi obsługiwać kontrolę wersji

Dlatego też na wstępnie odrzuciłem formaty doc i sxw. Nie pozwalają na szybkie pokazanie zmian w dokumencie.

DocBook

Mój ulubieniec. DocBook jest standardem tworzenia dokumentacji, opisów oraz książek (wykorzystywanym między innymi przez O’reilly). Dokument jest tworzony w standardowym edytorze tekstowym przy pomocy znaczników XMLowych. Z pliku źródłowego DocBooka można wygenerować następujące typy plików: pdf, html, rtf.

Ponieważ plik źródłowy to zwykły plik tekstowy, bez problemu można go umieścić w systemie kontrolii wersji.

Moim zdaniem najlepiej nadaje się do tworzenia dokumentacji dla użytkowników, administratorów i API - czyli generalnie rzeczy które są opisywane pod koniec produkcji systemu.

Wiki

Wszelkie systemy popularnie zwane Wiki (najpopularniejszy z nich to MediaWiki na którym opiera się Wikipedia) pozwalają użytkownikom tworzyć i edytować strony internetowe www z poziomu przeglądarki WWW. Istnieje wiele systemów wiki, napisanych w różnych językach (perl, php). Moim faworytem jest wspomniane wcześniej MediaWiki oraz DokuWiki

Zaletą tego narzędzia jest to że informacje są widoczne na stronie odrazu, bez potrzeby transformacji do jakiegoś formatu (jak to miało miejsce w przypadku DocBooka), oraz brak potrzeby instalacji specjalnego oprogramowania. Większość mechanizmów wiki wspiera wersjonowanie stron.

W przypadku wiki pojawia się jednak jeden problem. Jeżeli nie zaplanujemy sensownej struktury dokumentu/dokumentów, znalezienie interesujących informacji może być bardzo trudne.

Moim zdaniem ten typ dokumentacji najbardziej nadaje się do API i dokumentacji wewnętrznej.

Nieśmiertelne komentarze

Są chyba najstarszym i jednym z najlepszych narzędzi dokumetacji. Dzięki nim można dokumentować pliki konfiguracyjne oraz kody programów. Łatwo je umieścić w miejscu do którego się odnoszą, przez co nie trzeba szukać w zewnętrznych dokumentach.

Niektóre języki programowania (Python, Ruby - RDoc ) wspierają tworzenie dokumentacji w formacie HTML na podstawie komentarzy. Łączy to zalety Wiki oraz komentarzy ponieważ mamy zarówno dokumentacje na stronie www jak i w kodzie.

Moim zdaniem najlepiej sprawują się przy tworzeniu dokumentacji API i dokumentacji wewnętrznej.

Podsumowanie

Wybrałem tylko te narzędzia ponieważ w mojej ocenie stanowią one komplet tego co może być potrzene. Oczywiście można wszystko udokumentować w komentarzach czy pliku Worda - ale przecież chodziło o narzędzia ułatwiające tworzenie dokumentacji.

Jeżeli znacie jakieś inne narzędzia, chętnie sie z nimi zapoznam.

Dlaczego warto używać narzędzi ułatwiających “deployment” ?

środa, kwiecień 26th, 2006 by Paweł Rutkowski

Tworząc oprogramowanie bardzo rzadko zastanawiamy się nad procesem przenoszeniem zmian z wersji testowej do produkcyjnej - tzw. deploymentem. W małych systemach zazwyczaj wystarczy przegrać pliki z jednego katalogu do drugiego i załatwia to całą sprawę. Natomiast w większych systemach proces ten jest trudniejszy, ponieważ wymaga bardziej skomplikowanych operacji (np.: aktualizacji struktury bazy danych).

Narzędzia ułatwiające ten proces często dostarczają mechanizmy pozwalające na automatyczną modyfikację plików konfiguracyjnych (np.: definjujących dostęp do bazy), import skryptów SQL do bazy, czy też poprostu wykonanie pewnych poleceń systemowych. Szczególnie jest to przydatne kiedy między wersją testową a produkcyjną są dość znaczne różnice w konfiguracji (np.: ustawienia dotyczące scieżek, cache’a, wersji używanych programów itd). Bez narzędzi wspomagających przeniesienie zmian z jednej wersji do drugiej, łatwo jest zapomnieć o naniesieniu zmian co może spowodować błędne działanie (czy całkowite unieruchomienie) systemu.

Z jakich narzędzi zatem można skorzystać ?

Jednym z podstawowych narzędzi wspomagających deployment może być system kontroli wersji (np.: subversion czy cvs). Dzięki niemu możemy prawie że dowolnie modyfikować wersję testową, zapisać zmiany w repozytorium, następnie zaś w zaktualizwować pliki w innym katalogu.

Drugim narzędziem, bardziej rozbudowanym, jest Phing. Jest to system bardzo rozbudowany. Pozwala na tworzenie zadań, definiowanie zależności międzynimi, wykonywanie poleceń systemowych i wiele innych. Dzięki rozbudowanym mechanizmom, można stworzyć system który spowoduje iż wprowadzenie zmian nie będzie wymagało dużego nakładu pracy (poza skonfigurowaniem Phinga i zdefiniowaniem odpowiednich procedur).

Dwa poprzednie rozwiązania są uniwersalne - można je zastosować praktycznie do dowolnego systemu. Trzecie narzędzie które chciałbym przedstawić jest przeznaczone dla RubyOnRails a nazywa się Capistrano. RubyOnRails jest “fabrycznie” przygotowany do prostego deploymentu. Nawet bez Capistrano jest on przygotowany do przechowywania oddzielnej konfiguracji dla wersji testowych oraz produkcyjnych. Wśrób funkcji które udostępnia Capistrano warto wymienić:

  • Automatyczny deployment na inna maszynę (poprzez ssh+svn)
  • Przeprowadzenie migracji bazy danych (migracja bazy danych w RubyOnRails to temat na osobny artykuł, ponieważ rozwiązanie jest bardzo ciekawe)
  • Rollback - jeżeli po uruchomeniu nowej wersji produkcyjnej pojawi się jakiś błąd to jednym poleceniem możemy powrócić do poprzedniej wersji
  • Obsługę farm serwerów

Powyżej wymieniłem tylko kilka narzędzi (zaczynając od najprostszego). Istnieje wiele systemów które w mniejszym lub większym stopniu nadadzą się do tego. Można wyprobować znany z sytemów uniksowych make lub (z tego co wiem przeznaczony dla Javy) ANT czy poprostu napisać skrypt w ulubionym języku programowania.

Jeżeli ktoś z Was zna inne narzędzia przydatne podczas deploymentu to niech podzieli się wiedzą - chętnie je poznam.

Jak negocjować kontrakt (nie tylko IT) ?

środa, kwiecień 5th, 2006 by Paweł Rutkowski

Pewnie duża część z Was wielokrotnie spotkała się z problemem negocjacji treści umowy czy ogólnie warunków (zarówno z Klientem jak i Wykonawcą). W większości przypadków nie jest to prosta sprawa. Nie pododam tu recepty dotyczącej udanych negocjacji bo to jest raczej niemożliwe, natomiast podam kilka rad. Rad które przydadzą się zarówno Klientom jak i Wykonwacom.

Zaufanie - jest to moim zdaniem jeden z podstawowych czynników negocjacji. Strony powinny wierzyć w dobrą wolę drugiej strony. Jeżeli takie (napewno ograniczone na początku) zaufanie się nie pojawi, bardzo trudo prowadzi się rozmowy. Na każdy argument pojawi się kontrargument - np.:

Wykonawca
“Wykonaliśmy takie samo oprogramowanie dla XXX w ciągu 180 dni co potwierdzają referencje”
Klient
“Ale to wcale nie znaczy że nam wykonacie w tym terminie”

Szansa że wdrożenie drugi raz identycznego oprogramowania w identycznym terminie nie powiedzie się jest bardzo nieprawdopodobna - przecież część modułów jest już napisana.

Negocjuj tylko z osobami decyzyjnymi - nie rozmawiaj z pośrednikami czy podwładnymi. Mogą się oni zawsze wycofać ze swoich zobowiązań, lub ich szefowie powiedzą że nie byli oni uprawnieni do podejmowania decyzji. Dodatkowo unikniesz problemów związanych ze stronniczym przekazywaniem informacji (przecież pracownik się nie przyzna przełożonemu że zrobił błąd).

Szanuj czas drugiej strony - zawsze proponuj termin spotkania i poproś o jego potwierdzenie. Przy dużych kontraktach podczas negocjacji pojawia się kilka osób i mogą mieć problem z ustaleniem wspólnego terminarza. Nie przekładaj także spotkań w ostatniej chwili - stawia Cię to w bardzo złym świetle (jeżeli natomiast uświadczysz takiej sytuacji od drugiej strony, radze mieć się bardzo na baczności podczas reszty negocjacji).

Pytaj o wyjaśnienia - jeżeli czegoś nie rozumiesz - pytaj. Jeżeli boisz się zapytać wprost “O co Panu chodzi ?”, możesz użyć sformułowania “O ile dobrze Pana zrozumiałem, chodzi Panu o …” - dzięki temu uzyskasz potwierdzenie dobrej interpretacji informacji. Dodatkowo jeżeli negocjujesz umowę żądaj wyjaśnień dlaczego uważają wprowadzenie konkretnych zapisów za konieczne - będziesz miał szanse zrozumieć motywy drugiej strony.

Nie negocjuj z pozycji siły - nawet jeżeli jesteś w dużo lepszej pozycji, nie dawaj tego do zrozumienia drugiej stronie. Pomimo tego że są pewne szkoły negocjacji które uznają tą taktykę za efektywną niesie ona ze sobą duże zagrożenie. Po pierwsze wzbudzasz u drugiej strony poczucie że uważasz ich za gorszych, po drugie zaś może się okazać że po takim przedstawieniu warunków druga strona wstanie i powie “w takim razie dowidzenia”. Oczywiście są sytuacji kiedy wyjście z pozycji siły jest bardzo wskazane, ale są one rzadkie.

Oto kilka rad które mogą pomóc w udanych negocjacjach. Część z nich jest pewnie oczywista, innych oczywistych nie poruszyłem. Chętnie zapoznam się z Waszymi radami dotyczącymi negocjacji w projektach IT (i nie tylko).

Czy edytor WYSIWYG w systemach CMS rozwiązują wszystkie problemy Klientów ?

środa, marzec 29th, 2006 by Paweł Rutkowski

Wyjaśnienie

Krótkie wyjaśnienie dotyczące skrótów, które mogą nie być dla wszystkich zrozumiałe:

  1. CMS - ang. Content Management System czyli system zarządzania treścia. Są to systemy których zdaniem jest ułatwienie zarządzania treścią (”content”) stron WWW
  2. edytor WYSIWYG - w kontekście systemów zarządzania treścią jest to specjalne pole w którym wpisujemy tekst który ma zostać wyświetlony na stronie. Dodatkowo możemy dokonać formatowania tekstu (pogrubienie, podkreślenie). W przypadku bardziej zaawansowanych edytorów możliwości te mogą być rozszerzone o wstawianie obrazków, linków czy innych elementów.

Wstęp

Od jakiegoś czasu obserwuje znaczący wzrost liczby Klientów którzy w specyfikacjach systemów internetowych umieszczają mniejwiecej takie sformułowanie:

“Wszelkie teksty, opisy, materiały [tutaj mamy wyliczanke - przyp. aut.] muszą być edytowalne przez nie techniczny personel firmy. Dodatkowo edycja powinna być równie prosta co obsługa typowego edytora tekstu.”

Jest to oczywiste stwierdzenie że Klient oczekuje wdrożenia systemu CMS. Jednak zazwyczaj pojawia się kilka aspektów które przemawiają za tym aby tego nie robić. Troche będe generalizował, ale zazwyczaj się one sprawdzają:

  1. Klienci tak na prawde rzadko uaktualniają strony
  2. Klienci przywiązują zbytnią wagę do formy a nie treści
  3. Klient nawet po otrzymaniu bardzo dobrego systemu CMS, zleci jego obsługę pracownikom technicznym.
  4. Koszt wdrożenia dobrego systemu CMS jest wysoki w stosunku do korzyści.

Jednak te argumenty zazwyczaj nie docierają do Klientów. Jest to spowodowane tym że posiadanie CMS jest trendy. Praktycznie każda firma wdrażająca systemy internetowe, zaczyna prezentacje swoich produktów od pokazania ładnego, “Wordowego” edytora i opowiadania jakie to jest cudowne narzędzie. Natomiast nikt nie mówi o wadach tych rozwiązań.

Aby lepiej zrozumieć istotę problemu i dlaczego Klientom tak bardzo zależy na posiadniu systemu zarządzania trescią (oprócz tego że jest to trendy) musimy się trochę cofnąć w czasie.

Zamieszchłe czasy zarządzania stronami

Po tym jak wszelkiej maści specjaliści od marketingu poznali możliwości internetu, zaraz chcieli umieszczać wszystkie swoje pomysły w sieci. Jednak trafili na “drobny” opór - powiedziałbym że techniczny. Mianowicie ich wiedza nie pozwalała im na samodzielne tworzenie i zarządzanie stronami. Jeżeli chcieli cokolwiek zmienić na stronie czy coś dodać musieli zwracać się z prośbą do informatyka lub też zewnętrznej firmy. Pewnie nie było by w tym nic złego gdyby nie fakt że zaraz po publikacji nowej treści była ona wielokrotnie zmieniana. Niestety Klienci bardzo często werbalizują swoje potrzeby dopiero jak coś zostanie wykonane i pokazane.
Dodatkowo pojawiały się błędy wynikające z niezrozumienia (typu.: “Logo miało być pod zdjęciem a nie nad”). Powodowało to zarówno frustrację webmasterów którzy musieli kilkakrotnie wykonywać tą samą pracę jak i specjalistów od marketingu których pomysły nie zawsze były realizowane zgodnie z ich założeniami.

Jednak wraz z rozwojem technologii, pojawiły się narzędzia pozwalające na prostą edycję stron WWW.

A wszystko zaczeło się od FrontPage…

Jednym z pierwszych edytorów WYSIWYG był Microsoft FrontPage - pozwalał on na stosunkowo prostą edycję materiałów na stronach WWW. Nie tylko pojedyńczych podstron ale także całych witryn. Użytkownik mógł sobie “wyklikać” całą strone - na dodatek odrazu widział jak to będzie wyglądać.
Niestety cena wygody była wielka. Po pierwsze: kod HTML generowany przez niego był koszmarny. Nie dość że nie trzymał się żadnych standardów to dodatkowo był nadmiernie
obszerny. Po drugie: kod wygenerowany przez FrontPage’a działał praktycznie tylko w Internet Explorerze.
Wielu firmom / osobom jednak powyższe wady nie przeszkadzały i rozpoczeła się “mania klikalnego tworzenia stron”.

FronPage był jednak osobnym produktem i pozwalał tylko na edycję statycznych stron. Rozwiązaniem stały się edytory “osadzane” na stronach WWW. Zazwyczaj są napisane w języku JavaScript choć zdażają się także rozwiązania jako ActiveX oraz aplety Java.

Ich zaletą była możliwość łatwej integracji z formularzami WWW.

Rodzą sie CMSy

Systemy zarządzania trescią powstały razem z dynamicznymi stronami WWW. Początkowo nie miały służyć bezpośrednio Klientom - ułatwiały prace webmasterom którzy nie musieli tworzyć setek plików które przy każdej zmianie trzeba było edytować.

Jednak skoro można już było zintegrować prosty edytor z formularzem na stronie WWW, to już nie wiele więcej wysiłku kosztowało programistów napisanie systemu który pozwalał by zarówno na edytowanie treści jak i ich proste dodawanie. Systemy zarządzania treścią zaczeły pojawiać się masowo na rynku. Ponieważ na rynku najważniejszy jest Klient, większość z nich poszła w jego stronę maksymalnie uproszczając wszelkie czynność - nie wymagały one juz żadnej specjalistycznej wiedzy.

Święty gral czy przekleństwo ?

Takich możliwości brakowało działom marketingu. Każdy teraz chce mieć CMS do strony (który częstokroć jest dużo droższy niż stworzenie strony i jej aktualizacja przez zewnętrzną firmę) wraz z edytorem WYSIWYG.

Żadne argumenty nie potrafią odciągnąć Klienta od tego trendu.
Edytory WYSIWYG mają jednak podstawową wadę - pozwalają na stworzenie kodu podobnie koszmarnego, jak kod wygenerowany przez FrontPage’a, który jest zupełnie nie zgodny ze standardami W3C.

Kod takiej strony szybko rozrastał się i powodował jej wolne ładowanie. Pozatym Klient nie obeznany w tajnikach budowy stron może dzięki takiemu edytorowi spowodować całkowite “rozlecenie” się layoutu poprzez złe wstawienie różnych elementów (np.: za dużej grafiki, czy też źle sformatowanej tabelki).

Dodatkowo taka strona ma bardzo duże szanse być źle oceniona przez Google (ze względu na niekorzystny stosunek treści do kodu HTML)

Może podejść do tego inaczej…

WYSIWG to moim zdaniem zła droga. Zaproponuje inne roziwązanie które uważam za całkowicie wystarczające dla większości Klientów.
Jest one jednak troche trudniejsze w implementacji, jednak pozwala na zachowanie zarówno dobrej funkcjonalności oraz zgodności ze standardami.

Polega ona na opracowaniu zestawu znaczników, którymi będą musieli się posługiwać Klienci podczas edycji tekstu. Chcąć pogrubić tekst będą musieli napisać przykładowo

{BOLD}tekst{KONIEC_BOLD}

poźniej takie znaczniki będą odpowiednio interpertowane przez oprogramowanie i przekształcane do HTMLa.

Zalety są następujące:

  1. Klient wstawi kodu na który mu nie pozwolimy
  2. “Rozlecenie” się layoutu jest zminimalizowane
  3. Kod nie będzie się rozrastał i pozostanie zgodny ze specyfikacją

Stosując jednak to rozwiązanie, należy pamietać o umieszczeniu gdzieś przy edytorze “sciągawki” dzięki której Klient będzie mógł podejżeć zestaw znaczników i efekty ich użycia.

Na tależu…

Nie trzeba odkrywać koła na nowo. Istnieją już gotowe narzędzia do obsługi mechanizmu który zaproponowałem. Moim faworytem jest Textile - można łatwo się do niego przyzwyczaić oraz istnieją już gotowe klasy do obsługi tego formatu w wielu językach. Kolejnym formatem którego można użyć jest BBCode. Jest to standard używany w dość popularnym forum phpBB. Niestety nie wiem czy isnieją gotowe implementacje interpertatora dla innych języków.

Istnieją także inne gotowe formaty, jednak bardziej chodziło mi o pokazanie wad WYSIWYG i możliwości zastąpienia ich prostszymi (bardzo często lepszymi) mechanizmami.