Articles, Tips & tricks

Wstawianie kawałków kodu na wordpress.com

W przypadku platformy wordpress mamy do wyboru bardzo wiele różnych wtyczek do formatowania i kolorowania składni kodu. Niestety nie możemy z tego wszystkiego korzystać, jeżeli hostujemy swój blog na wordpress.com (platforma z gotowymi instalacjami wordpressa, niestety z ograniczonymi możliwościami ręcznych ustawień).
Mimo wszystko w przypadku wordpress.com cały czas mamy dostęp do całkiem fajnej wtyczki umożliwiającej formatowanie kodu, ale jej użycie wcale nie jest takie oczywiste.

Przede wszystkim nie można dać się nabrać domyślnemu edytorowi postów który podpowiada jak oznaczyć kawałek kodu:
2017-12-14_21-18-37

Niestety znacznik <code></code> wstawia po prostu kawałek niesformatowanego tekstu:
public void HelloWorld()
{
var str = "Hello world";
Console.WriteLine(str);
}

Do wstawiania fragmentów kodu służy znacznik [code] [/code]:

public void HelloWorld()
{
    var str = "Hello world";
    Console.WriteLine(str);
}

Taki kawałek kodu możemy przy okazji dodatkowo skonfigurować przy użyciu następujących parametrów:

  • [code language="LANG"] … [/code] – koloruje składnię, LANG to predefiniowany język programowania, np.: csharp, css, javascript, html, powershell lub po prostu zwykły tekst: text
    public void HelloWorld()
    {
        var str = "Hello world";
        Console.WriteLine(str);
    }
  • [code collapse="true" title="Mój hello world"] … [/code] – kod będzie domyślnie zwinięty
    public void HelloWorld()
    {
        var str = "Hello world";
        Console.WriteLine(str);
    }
  • [code gutter="false"] … [/code] – schowaj numerowanie linii
    public void HelloWorld()
    {
        var str = "Hello world";
        Console.WriteLine(str);
    }
  • [code highlight="3,4"] … [/code] – podświetla linie 3 i 4
    public void HelloWorld()
    {
        var str = "Hello world";
        Console.WriteLine(str);
    }

Więcej szczegółów znajdziesz tutaj: https://en.support.wordpress.com/code/posting-source-code/

Czy w takim razie znacznik <code></code> jest bezużyteczny? Nie, użyłem go nawet kilka razy w tym poście, ale wyłącznie do oznaczania niesformatowanych kawałków tekstu, nazw zmiennych, parametrów itp..

Advertisements
Command line, Tips & tricks

Cleaning up git branches

In order to keep your git environment in a good shape you need to clean up branches from time to time. It is especially difficult in case of build servers which complete pull requests on behalf of developers (like VSTS). It’s easy when you have just a few branches to review, but if you forget to do some cleaning for longer while then it’s getting painful. For sure you do not want to remove effects of your work by accident during cleanup.

Git branch on steroids

There is a very cool option for well-known git branch command: -vv (very verbose). It will enrich flat list of branches with lots of extra information (see below):

branch1
Instead of just branch names we get (in the following order):

  • Branch name (current one is marked with star)
  • Last commit hash
  • Remote references (if exist)
  • Last commit message

The coolest thing is reference to remote-tracking branches. What’s more it shows if remote-tracking branch is ahead/behing remote or remote branch is gone. You can use that information to split cleanup process into the following steps:

1. Fetch branch information

You need to run the following command in order to remove remote-tracking branches that no longer exist on remote repository and refresh information about ahead/behind commits:

git fetch --all --prune

2. Remove branches that no longer exist on remote

When VSTS completes pull-request, it removes (by default) the source feature branch. If you forget to remove corresponding local branch, then it gets orphaned and it’s a perfect candidate to clean up.

branch2

In order to do a bulk clean up, run the following command (you will find command line reference in the end of this post):
git branch -vv | grep ": gone\]" > .to-remove && vim .to-remove

This should extract all orphaned branches to a file and let you review/edit that file before real clean up. After that you should use another command to remove them:

grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

3. Remove unpushed branches

You may also need to clean up some temporary or deprecated unpushed branches. Run the following command to find (and review) all local branches that have not been pushed to remote.

git branch -vv | grep -v "\[origin.*\]" > .to-remove && vim .to-remove

After that you can remove them all same as before:
grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

4. Remove branches with existing remote branch

You should find them using the following command:

git branch -vv | grep "\[origin.*\]" > .to-remove && vim .to-remove

I would think twice before removing branches that are ahead of remote version, but the rest can be safely reviewed and cleaned up (all in all up-to-date version exists on remote)

2017-12-03_11-18-40

Again, in order to bulk remove branches you run the familiar command.

grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

CAUTION: If you want, you can also remove branches on remote using the following comand (do it carefully):

grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git push origin --delete

All-in-one

If you have only a few branches to clean up you do not have to do steps from 2 to 4 one-by-one. Instead edit the whole list of branches before removing:

git branch -vv > .to-remove && vim .to-remove

And finally remove them:
grep ".*" .to-remove | cut -c 3- | awk '{print $1}' | xargs git branch -D

Branch naming

Personally I add workitem ID to the name of my branches (see screenshots). On one hand it’s easier to connect pull-requests with corresponding work item. On the other hand it’s handy while removing branches. I can extract work item IDs of my branches and check which of them are still in-progress and I should wait witch cleaning them up.

In order to get a comma-separated list of workitem IDs behind my branches I use the following command:

git branch | cut -c 3- | grep -oP "\d{3,}" > .tmp && vim .tmp && paste -sd "," .tmp

Command line reference (Windows)

| – redirect output of one command as input to another command
&& – run another command without redirecting output/input
> – redirect output to a file
grep ": gone\]" – find lines containing given phrase “: gone]
grep -v "\[origin.*\]" – find lines not containing phrase “[origin.*]” (.* means “anything”
grep ".*" .to-remove – print all lines of file “.to-remove
cut -c 3- – substring from input, from 3rd character in line to the end of line
awk '{print $1}' – print first column of text (columns are separated by whitespaces)
xargs – use input as parameter to given command
grep -o – print only the matching part (not the whole line)
grep -P – use PERL regular expression instead of basic (allows usage of \d)
git branch -D – force delete unmerged branch (needed if build server squash commits when completing pull request)
paste -sd "," .tmp – read .tmp file and join lines using comma

grep, cut, awk, xargs, paste are linux commands but they are distributed with Git for Windows (same folder as git.exe)

Programming (back-end)

Ograniczanie listy zadań w async/await

Załóżmy, że mamy do pobrania długą listę plików. Wiadomo, że najlepiej byłoby ściągać wiele plików jednocześnie. Z drugiej strony ze względu na ograniczenia sieciowe zamiast zaczynać 1000 operacji i nie skończyć ani jednej, lepiej podzielić listę plików na kawałki i ściągać je np. po 10 jednocześnie (analogiczne do innych operacji asynchronicznych).

W przypadku TPL ograniczanie listy operacji równoległych jest proste:

Parallel.ForEach(
    listOfUris,
    new ParallelOptions { MaxDegreeOfParallelism = 10 },
    fileUri => { webClient.DownloadString(fileUri); }
);

Takie rozwiązanie powoduje, że użyte zostanie 10 wątków, które cały czas będą czekać na operację ściągnięcia pliku. To nie jest najlepsze rozwiązanie dla operacji I/O. W tym przypadku lepiej wykorzystać async/await. I tu mała dygresja. Wersja podobna do tej z TPL nie działa tak jak trzeba:

using (var httpClient = new HttpClient()){
   foreach(var fileUri in listOfUris)
   {
      await httpClient.GetStringAsync(fileUri);
   }
}

To powoduje właśnie synchroniczne ściąganie plików – jeden po drugim. Jeśli chcemy ściągać wszystkie pliki jednocześnie, musimy przerobić ten fragment na:

var tasks = new List<Task<string>>();
using (var httpClient = new HttpClient())
{
   foreach (var fileUri in fileUris)
   {
      var task = httpClient.GetStringAsync(fileUri);
      tasks.Add(task);
   }
}
Task.WhenAll(tasks);

Dalej mamy foreach, który jednak zbiera listę tasków na które czekamy. W takiej formie dodanie ograniczenia na liczbę równoległych requestów polega na użyciu SemaphoreSlim:

            var tasks = new List<Task<string>>();
            var semaphore = new SemaphoreSlim(initialCount: 10);
            using (var httpClient = new HttpClient())
            {
                foreach (var fileUri in fileUris)
                {
                    await semaphore.WaitAsync();
                    var task = httpClient.GetStringAsync(fileUri);
                    task.ContinueWith(x => semaphore.Release());
                    tasks.Add(task);
                }
            }
            Task.WhenAll(tasks);

Szybko przeanalizujmy działanie tego kodu: dopóki zadań jest wiecej niż 10 to SemaphoreSlim ogranicza listę ściąganych operacji. W tym przypadku nie wychodzimy w ogóle z pętli foreach. Natomiast po wyjściu z pętli dochodzimy do Task.WhenAll() i w ten sposób czekamy pozostałe zadania.

Command line: VIM

VIM: sorting and removing duplicates

There is one very useful command in VIM that allows to do both sorting of lines in current buffer and removing duplicated lines.

In order to do sorting type the following command:
:sort
gif1

In order to do both sorting and removing duplicates type the following command:
:sort u
gif2

Other useful options of :sort command:
:sort! – sort in reverse
:1,100sort – sort first 100 lines
:sort n – numeric sort (“20” goes before “100”)
:sort /{pattern}/ – sort based on pattern found in line (see an example here)
:help :sort – see help for sorting

Continuous delivery

Continuous Integration vs Continuous Delivery vs Continuous Deployment

Wiele razy słyszałem jak ludzie mylą Continuous Delivery z Continuous Deployment dlatego zdecydowałem się tutaj pokazać różnice, zaczynając przy okazji od Continuous Integration. Bez obaw, postaram się unikać słownikowych definicji.
Wyobraźmy sobie, że chcemy ocenić poziom zaawansowania procesu wytwarzania oprogramowania jaki obowiązuje w naszym projekcie. Możemy tu wydzielić następujące etapy:

Level 0: None

Wszystko robione jest ręcznie. Programista bierze skądś kod źródłowy (odpowiedni folder na dysku, może nawet jakieś swoje repo w sieci), kompiluje i tworzy paczkę, którą może zainstalować/uruchomić na docelowym środowisku. Jego własna ekspercka wiedza potrafi ocenić czy paczka którą stworzył jest “dobra” (może nawet bardzo skrupulatnie przetestować to co zrobił). Jeżeli nad kodem pracuje więcej niż jedna osoba, to co jakiś czas synchronizuje swoje zmiany z innymi członkami zespołu, rozwiązuje powstałe konflikty i ustala czy nic się nie zepsuło.
Wydawać by się mogło, że nikt już z tego nie korzysta, ale przecież właśnie tak zaczynał się kiedyś każdy domowy/studencki projekt. Nie można mówić, że to coś złego (pewnie gorsze byłoby tworzenie od początku skomplikowanego procesu aby zrobić prosty programik).

Level 1 (Normal): Continuous Integration

Każda zmiana w kodzie jest integrowana z resztą systemu w sposób ciągły. Ciągły, czyli jak najczęściej to możliwe – czasem na poziomie poszczególnych commitów, czasem pull-requestów. W jaki sposób można ocenić, że integracja się powiodła? Po pierwsze dołączenie zmiany do reszty systemu jest możliwe (nie ma konfliktów w systemie kontroli wersji kodu). Po drugie zmiana nie psuje działania reszty systemu (tutaj potrzebne jest uruchomienie testów automatycznych, przynajmniej testów jednostkowych). Integracja odbywa się w sposób ciągły, za każdym razem kiedy zmiana trafia do publicznego repozytorium. Czy jak ktoś wysyła zmiany na serwer raz w tygodniu to dalej jest continuous integration? Tak, ale to ewidentne proszenie się o konflikty. Od tego jest Continuous Integration (CI) aby integrować się jak najczęściej i rozwiązywać malutkie konflikty a nie raz w tygodniu przepisywać koncepcję na od nowa.

Level 2 (Hard): Continuous Delivery

Celem jest ciągłe posiadanie stabilnej paczki którą możemy w prosty sposób wrzucić na produkcję (środowisko docelowe). Oprócz wszystkiego co daje nam CI musimy też mieć możliwość łatwego przeprowadzenia deploymentu (najczęściej one-click deployment). Duży nacisk kładzie się właśnie na automatyzacje wszelkich zmian w konfiguracji aplikacji, migrowanie baz danych, tworzenie/podłączanie nowych zasobów (serwisów, baz, kolejek itp). W związku z częstszym wdrażaniem paczek na produkcję należy ulepszyć proces przywracania poprzedniej wersji aplikacji (czasem błędy wychodzą dopiero na produkcji). W związku z tym często rozszerza się sposób kontroli wersji wprowadzając GitFlow lub jakąś własną implementację tego procesu. Aby ograniczyć ilość pracy związanej z ciągłym testowaniem paczki przed wdrożenie rozbudowuje się testy automatyczne. W przypadku aplikacji webowych wprowadza się testy API/UI aby uniknąć regresji i testować nowe funkcjonalności z punktu widzenia użytkownika (również w połączeniu z innymi zależnościami takimi jak połączenie z bazą danych i innymi serwisami).
Koszt jest dużo wyższy niż w przypadki CI, ale zaletą jest możliwość dostarczania nowej wersji aplikacji w krótkich odstępach czasu (w przypadku SCRUMa co sprint lub nawet częściej).

Level 3 (Expert): Continuous Deployment

To już jest jazda bez trzymanki połączona z grą na akordeonie. Celem jest aby każda zmiana od razu trafiała na środowisko produkcyjne. Nie ma tu miejsca na każdorazową decyzje człowieka jeśli chodzi o wdrażanie nowej paczki. W związku z tym konieczne jest rozbudowanie systemu monitorowania aplikacji i wczesne zgłaszanie błędów. Lepiej żeby proces sprawdzania paczki trwał dłużej i zatrzymał się przy końcu niż przepuścił wersję która zawiera błąd i psuje dane użytkownika. Dlatego w tym przypadku oprócz dopieszczenia testów automatycznych wprowadza się sprawdzanie code coverage bliskiego 100% (nie można pozwolić, aby na produkcję trafił niepewny kawałek kodu). Oczywiście nie da się zaimplementować gotowej funkcjonalności przy pomocy kilku commitów, dlatego bardzo często stosuje się tzw. “feature switches” które pozwalają włączyć funkcjonalność w sposób kontrolowany tylko określonym użytkownikom lub stopniowo (tzw “canary release”). Deployment też należy udoskonalić w taki sposób, aby nie wpływał na sposób działania aplikacji (system musi działać bez widocznych przerw).
Continuous Deployment pozwala na zbudowanie najlepszej relacji między twórcą aplikacji a jej użytkownikami, jednak koszt wprowadzenia takiego rozwiązania jest ogromny. Często mogą sobie na to pozwolić tylko największe serwisy. W tym samym miejscu jest to programistyczne El Dorado dla osób zajmujących się procesem wytwarzania oprogramowania.

Myślę, że coś takiego jak Level 4 już nie istnieje, ale gdyby przyszło wam coś do głowy, to czekam na komentarze.

Programming (back-end), Programming (front-end)

Czy można ignorować testy?

Wiele razy słyszałem, że jak ma się ignorować testy to już lepiej je usunąć. Dlaczego? Bo bardzo łatwo podjąć decyzję o zignorowaniu testu (dodaniu jednego atrybutu czy tam literki “x” w przypadku JavaScript) a trudniej podjąć decyzję o od-ignorowanie testu. Tutaj znajdzie się już milion powodów, żeby tego nie robić: a bo nie ma czasu, a bo to trzeba zmienić coś innego co już działa, a bo ten test to od początku pisała jakaś ciamajda i trzeba go przepisać i to potrwa.
A więc czemu usunąć? Bo i tak nie są uruchamiane i starzeją się z każdą chwilą. To jest “Dead code” który przy okazji maskuje inny “dead code” (normalnie na klasie dostalibyśmy “0 references”, ale przecież jest używana w tym martwym teście).

Co w takim razie robić? Mi do głowy przychodzą następujące pomysły:

  • Naprawić test
  • Napisać test od nowa
  • Usunąć test i przy testowanej metodzie dodać “TODO” (może ktoś te TODO przegląda)
  • Dodać do listy długu technicznego naprawę tego testu (my używamy specjalnych tasków w backlogu)

A co jak już musisz ignorować, bo chcesz naprawić ten nieszczęsny test ale czujesz na plecach oddech Product Ownera? Wtedy można ignorować test, ale mądrzej:

  • Niektóre frameworki pozwalają dodać datę do kiedy test będzie nieaktywny, np..
    [Ignore("XYZ external service down", Until = "2017-12-14 12:00:00Z"]
  • Jeśli aktualnie nie możesz skorzystać z takiego atrybutu, zawsze możesz zastosować prosty trik, dodając na początku testu:
    If (DateTime.Now > new DateTime(2017, 12, 14)) { return; }
    Ważne jest to, aby Ci system sam przypomniał, że masz coś do zrobienia.

Jeśli znajdziesz w swoim frameworku fajny sposób na czasowe wyłączenie testów to podziel się w komentarzu. Jeżeli jesteś innego zdania – tym bardziej.

Articles

[PL] Przechodzę na polski / Switching blog language to Polish

For english-speaking readers – I decided to switch blog language to Polish as an experiment. I want to check if that will increase freaquency of publishing new posts or increase number of visitors. At the same time some universal posts may still be in English so don’t disconnect.

Wierzę, że “Polcy i Polaki” zrozumieli. Raz kozie śmierć! Chciałbym sprawdzić, czy po polsku uda się pisać szybciej a w rezultacie częściej i więcej. Mimo wszystko mój styl bycia, żartowania i pisania jest właśnie polski. Poza tym część rzeczy o których chcę pisać odnosi się konkretnie do polskiego środowiska, pracy, prawa i trudno byłoby to wszystko pisać/czytać w języki angielskim.
Na początku zakładałem, że pisząc po angielsku piszę do wszystkich. Ale jak coś jest do wszystkiego, to jak wiemy jest do niczego. Odnosząc to do bloga, czasem mam wrażenie, że jest do nikogo, nie ma życia. A chciałbym właśnie pisać do konkretnych grup ludzi. A może oni też woleliby komentować pod artykułem po polsku? Zobaczymy.
Z drugiej strony, jeżeli będę chciał pisać o rzeczach wyjątkowo technicznych, to pewnie zrobię to po angielsku – nie ma co pisać po polsku i co chwilę wciskać jakiś makaronizm. Poza tym jeśli chciałbym odesłać kogoś to takich uniwersalnych treści, to jednak będzie łatwiej.

Zobaczymy co z tego wyjdzie. Nie wykluczam, że taka wersja bloga zacznie mi przeszkadzać, że to wcale nie chodziło o język i wrócimy do angielskiego. A co mi tam, mój blog, nic nie tracę przecież. Kto się zgadza ręka w góre, plusik w komentarzu 🙂